<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "LagInfo.h"

float LagInfo::threshold = 0.0;
float LagInfo::max       = 0.0;

LagInfo::LagInfo(PlayerInfo *_info)
  : info(_info), lagavg(0), jitteravg(0), lostavg(0), lagalpha(1),
    jitteralpha(1), lostalpha(1), lagcount(0), laglastwarn(0), lagwarncount(0),
    pingpending(false), pingseqno(0), pingssent(0), lasttimestamp(0.0f) {
}

void LagInfo::reset()
{
  nextping       = info->now;
  nextping      += 10.0;
  lastupdate     = info->now;
}

int LagInfo::getLag()
{
  return int(lagavg * 1000);
}

void LagInfo::getLagStats(char* msg) {
  msg[0] = 0;
  if (!info->isPlaying() || !info->isHuman())
    return;

  // don't wait for ping to come back
  int lag	  = int(lagavg * 1000);
  if (pingpending) {
    float timepassed = info->now - lastping;
    int lastLag      = int((lagavg * (1 - lagalpha) + lagalpha * timepassed)
			   * 1000);
    if (lastLag > lag)
      lag = lastLag;
  }
  sprintf(msg,"%-16s :\t %3d +- %2dms", info->getCallSign(),
	  lag,
	  int(jitteravg * 1000));
  if (lostavg >= 0.01f)
    sprintf(msg + strlen(msg), " %d%% lost/ooo", int(lostavg * 100));
}

// update absolute latency based on LagPing messages
int LagInfo::updatePingLag(void *buf, bool &warn, bool &kick) {
  uint16_t _pingseqno;
  int lag = 0;
  nboUnpackUShort(buf, _pingseqno);
  if (pingseqno == _pingseqno) {
    float timepassed = info->now - lastping;
    // time is smoothed exponentially using a dynamic smoothing factor
    lagavg   = lagavg * (1 - lagalpha) + lagalpha * timepassed;
    lagalpha = lagalpha / (0.9f + lagalpha);
    lag      = int(lagavg * 1000);
    lagcount++;

    // warn players from time to time whose lag is > threshold (-lagwarn)
    if (!info->isObserver() && (threshold > 0) && lagavg > threshold
	&& lagcount - laglastwarn > 2 * lagwarncount) {
      laglastwarn = lagcount;
      lagwarncount++;
      warn = true;
      kick = (lagwarncount++ > max);
    } else {
      warn = false;
      kick = false;
    }
    lostavg     = lostavg * (1 - lostalpha);
    lostalpha   = lostalpha / (0.99f + lostalpha);
    pingpending = false;
  } else {
    warn = false;
    kick = false;
  }
  return lag;
}

void LagInfo::updateLag(float timestamp, bool ooo) {
  if (!info->isPlaying())
    return;
  if (ooo) {
    lostavg   = lostavg * (1 - lostalpha) + lostalpha;
    lostalpha = lostalpha / (0.99f + lostalpha);
  }
  // don't calc jitter if more than 2 seconds between packets
  if (lasttimestamp > 0.0f && timestamp - lasttimestamp < 2.0f) {
    const float jitter = fabs(info->now - lastupdate
			      - (timestamp - lasttimestamp));
    // time is smoothed exponentially using a dynamic smoothing factor
    jitteravg   = jitteravg * (1 - jitteralpha) + jitteralpha * fabs(jitter);
    jitteralpha = jitteralpha / (0.99f + jitteralpha);
    lostavg     = lostavg * (1 - lostalpha);
    lostalpha   = lostalpha / (0.99f + lostalpha);
  }
  lasttimestamp = timestamp;
  lastupdate    = info->now;
}

int LagInfo::getNextPingSeqno(bool &warn, bool &kick) {

  warn = false;
  kick = false;

  if (!info->isPlaying() || !info->isHuman())
    return -1;

  if (nextping - info->now >= 0)
    // no time for pinging
    return -1;

  pingseqno = (pingseqno + 1) % 10000;
  if (pingpending) {
    // ping lost
    lostavg   = lostavg * (1 - lostalpha) + lostalpha;
    lostalpha = lostalpha / (0.99f + lostalpha);
    if (!info->isObserver() && (threshold > 0)
	&& lagcount - laglastwarn > 2 * lagwarncount) {
      laglastwarn = lagcount;
      lagwarncount++;
      warn = true;
      kick = (lagwarncount++ > max);
    }
  }

  pingpending = true;
  lastping    = info->now;
  nextping    = info->now;
  nextping   += 10.0f;
  pingssent++;
  return pingseqno;
}

// update absolute latency based on LagPing messages
void LagInfo::updateLatency(float &waitTime) {
  if (!info->isPlaying() || !info->isHuman())
    return;
  float delta = nextping - info->now;
  if (delta < waitTime)
    waitTime  = delta;
}

void LagInfo::setThreshold(float _threshold, float _max) {
  threshold = _threshold;
  max       = _max;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>hopefully get lagstats display lined up properly horizontally<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named LICENSE that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/* interface header */
#include "LagInfo.h"

float LagInfo::threshold = 0.0;
float LagInfo::max       = 0.0;

LagInfo::LagInfo(PlayerInfo *_info)
  : info(_info), lagavg(0), jitteravg(0), lostavg(0), lagalpha(1),
    jitteralpha(1), lostalpha(1), lagcount(0), laglastwarn(0), lagwarncount(0),
    pingpending(false), pingseqno(0), pingssent(0), lasttimestamp(0.0f) {
}

void LagInfo::reset()
{
  nextping       = info->now;
  nextping      += 10.0;
  lastupdate     = info->now;
}

int LagInfo::getLag()
{
  return int(lagavg * 1000);
}

void LagInfo::getLagStats(char* msg) {
  msg[0] = 0;
  if (!info->isPlaying() || !info->isHuman())
    return;

  // don't wait for ping to come back
  int lag = int(lagavg * 1000);
  if (pingpending) {
    float timepassed = info->now - lastping;
    int lastLag = int((lagavg * (1 - lagalpha) + lagalpha * timepassed) * 1000);
    if (lastLag > lag)
      lag = lastLag;
  }
  sprintf(msg,"%s \t: %3d +- %2dms", info->getCallSign(),
	  lag, int(jitteravg * 1000));
  if (lostavg >= 0.01f)
    sprintf(msg + strlen(msg), " %d%% lost/ooo", int(lostavg * 100));
}

// update absolute latency based on LagPing messages
int LagInfo::updatePingLag(void *buf, bool &warn, bool &kick) {
  uint16_t _pingseqno;
  int lag = 0;
  nboUnpackUShort(buf, _pingseqno);
  if (pingseqno == _pingseqno) {
    float timepassed = info->now - lastping;
    // time is smoothed exponentially using a dynamic smoothing factor
    lagavg   = lagavg * (1 - lagalpha) + lagalpha * timepassed;
    lagalpha = lagalpha / (0.9f + lagalpha);
    lag      = int(lagavg * 1000);
    lagcount++;

    // warn players from time to time whose lag is > threshold (-lagwarn)
    if (!info->isObserver() && (threshold > 0) && lagavg > threshold
	&& lagcount - laglastwarn > 2 * lagwarncount) {
      laglastwarn = lagcount;
      lagwarncount++;
      warn = true;
      kick = (lagwarncount++ > max);
    } else {
      warn = false;
      kick = false;
    }
    lostavg     = lostavg * (1 - lostalpha);
    lostalpha   = lostalpha / (0.99f + lostalpha);
    pingpending = false;
  } else {
    warn = false;
    kick = false;
  }
  return lag;
}

void LagInfo::updateLag(float timestamp, bool ooo) {
  if (!info->isPlaying())
    return;
  if (ooo) {
    lostavg   = lostavg * (1 - lostalpha) + lostalpha;
    lostalpha = lostalpha / (0.99f + lostalpha);
  }
  // don't calc jitter if more than 2 seconds between packets
  if (lasttimestamp > 0.0f && timestamp - lasttimestamp < 2.0f) {
    const float jitter = fabs(info->now - lastupdate
			      - (timestamp - lasttimestamp));
    // time is smoothed exponentially using a dynamic smoothing factor
    jitteravg   = jitteravg * (1 - jitteralpha) + jitteralpha * fabs(jitter);
    jitteralpha = jitteralpha / (0.99f + jitteralpha);
    lostavg     = lostavg * (1 - lostalpha);
    lostalpha   = lostalpha / (0.99f + lostalpha);
  }
  lasttimestamp = timestamp;
  lastupdate    = info->now;
}

int LagInfo::getNextPingSeqno(bool &warn, bool &kick) {

  warn = false;
  kick = false;

  if (!info->isPlaying() || !info->isHuman())
    return -1;

  if (nextping - info->now >= 0)
    // no time for pinging
    return -1;

  pingseqno = (pingseqno + 1) % 10000;
  if (pingpending) {
    // ping lost
    lostavg   = lostavg * (1 - lostalpha) + lostalpha;
    lostalpha = lostalpha / (0.99f + lostalpha);
    if (!info->isObserver() && (threshold > 0)
	&& lagcount - laglastwarn > 2 * lagwarncount) {
      laglastwarn = lagcount;
      lagwarncount++;
      warn = true;
      kick = (lagwarncount++ > max);
    }
  }

  pingpending = true;
  lastping    = info->now;
  nextping    = info->now;
  nextping   += 10.0f;
  pingssent++;
  return pingseqno;
}

// update absolute latency based on LagPing messages
void LagInfo::updateLatency(float &waitTime) {
  if (!info->isPlaying() || !info->isHuman())
    return;
  float delta = nextping - info->now;
  if (delta < waitTime)
    waitTime  = delta;
}

void LagInfo::setThreshold(float _threshold, float _max) {
  threshold = _threshold;
  max       = _max;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>#include <osgGA/UFOManipulator>
#include <osgUtil/IntersectVisitor>

using namespace osgGA;

UFOManipulator::UFOManipulator():
            _t0(0.0),
            _shift(false),
            _ctrl(false)
{
    _minHeightAboveGround          = 2.0;
    _minDistanceInFront            = 5.0;

    _speedAccelerationFactor       = 0.4;
    _speedDecelerationFactor       = 0.90;

    _directionRotationRate         = 0.0;
    _directionRotationAcceleration = M_PI*0.00005;
    _directionRotationDeceleration = 0.90;

    _speedEpsilon                  = 0.02;
    _directionRotationEpsilon      = 0.0001;

    _viewOffsetDelta = M_PI * 0.0025;
    _pitchOffsetRate = 0.0;
    _pitchOffset = 0.0;

    _yawOffsetRate = 0.0;
    _yawOffset = 0.0;
    _offset.makeIdentity();

    _decelerateOffsetRate = true;
    _straightenOffset = false;

    _direction.set( 0,1,0);
    _stop();
}

void UFOManipulator::setNode( osg::Node *node )
{
    _node = node;

    if (getAutoComputeHomePosition())
        computeHomePosition();

    _home();
}

const osg::Node* UFOManipulator::getNode() const
{
    return _node.get();
}

osg::Node* UFOManipulator::getNode()
{
    return _node.get();
}

const char* UFOManipulator::className() const
{
    return "UFOManipulator";
}

void UFOManipulator::setByMatrix( const osg::Matrix &mat )
{
    _inverseMatrix = mat;
    _matrix.invert( _inverseMatrix );
}

void UFOManipulator::setByInverseMatrix( const osg::Matrix &invmat)
{
    _matrix = invmat;
    _inverseMatrix.invert( _matrix );
}

osg::Matrix UFOManipulator::getMatrix() const
{
        return (_offset * _matrix);
}

osg::Matrix UFOManipulator::getInverseMatrix() const
{
    return (_inverseMatrix * _offset);
}

void UFOManipulator::computeHomePosition()
{
    if( !_node.valid() )
        return;

    osg::BoundingSphere bs = _node->getBound();

    /*
       * Find the ground - Assumption: The ground is the hit of an intersection
       * from a line segment extending from above to below the database at its
       * horizontal center, that intersects the database closest to zero. */
    osgUtil::IntersectVisitor iv;
    osg::ref_ptr<osg::LineSegment> seg = new osg::LineSegment;
    osg::Vec3 A = bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2));
    osg::Vec3 B = bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2));

    if( (B-A).length() == 0.0)
    {
        puts( "DOH" ); fflush(stdout);
        return;
    }

    /*
    seg->set( bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2)),
              bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2)) );
              */
    seg->set( A, B );

    iv.addLineSegment( seg.get() );
    _node->accept(iv);

    // start with it high
    double ground = bs.radius() * 3;

    if (iv.hits())
    {
        osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(seg.get());
        for(osgUtil::IntersectVisitor::HitList::iterator hitr=hitList.begin();
            hitr!=hitList.end(); ++hitr)
        {
            osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
            if( fabs(ip[3]) < ground )
                ground = ip[3];
        }
    }
    else
    {
        osg::notify(osg::WARN)<<"UFOManipulator : I can't find the ground!"<<std::endl;
        ground = 0.0;
    }

    osg::Vec3 p(bs.center()[0], bs.center()[1], ground + (_minHeightAboveGround*1.25) );
    setHomePosition( p, p + osg::Vec3(0,1,0), osg::Vec3(0,0,1) );
}

void UFOManipulator::home(const osgGA::GUIEventAdapter&, osgGA::GUIActionAdapter&)
{
    _home();
}

void UFOManipulator::_home()
{
    if (getAutoComputeHomePosition())
        computeHomePosition();

    _position = _homeEye;
    _direction = _homeCenter - _homeEye;
    _direction.normalize();
    _directionRotationRate = 0.0;

    _inverseMatrix.makeLookAt( _homeEye, _homeCenter, _homeUp );
    _matrix.invert( _matrix );

    _offset.makeIdentity();

    _forwardSpeed = 0.0;
    _sideSpeed = 0.0;
    _upSpeed = 0.0;
}

bool UFOManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter &aa)
{

    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::KEYUP):
            _keyUp( ea, aa );
            return false;
            break;

        case(osgGA::GUIEventAdapter::KEYDOWN):
            _keyDown(ea, aa);
            return false;
            break;

        case(osgGA::GUIEventAdapter::FRAME):
            _frame(ea,aa);
            return false;
            break;

        default:
            return false;
    }

    return false;
}

void UFOManipulator::getUsage(osg::ApplicationUsage& usage) const
{
    usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>",        "Reset the viewing angle to 0.0");
    usage.addKeyboardMouseBinding("UFO Manipulator: <UpArrow>",         "Acceleration forward.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <DownArrow>",       "Acceleration backward (or deceleration forward");
    usage.addKeyboardMouseBinding("UFO Manipulator: <LeftArrow>",       "Rotate view and direction of travel to the left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <RightArrow>",      "Rotate view and direction of travel to the right.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>",        "Brake.  Gradually decelerates linear and rotational movement.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/UpArrow>",   "Accelerate up.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/DownArrow>", "Accelerate down.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/LeftArrow>", "Accelerate (linearly) left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/RightArrow>","Accelerate (linearly) right.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/SpaceBar>",  "Instant brake.  Immediately stop all linear and rotational movement.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/UpArrow>",    "Rotate view (but not direction of travel) up.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/DownArrow>",  "Rotate view (but not direction of travel) down.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/LeftArrow>",  "Rotate view (but not direction of travel) left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/RightArrow>", "Rotate view (but not direction of travel) right.");
}

void UFOManipulator::_keyUp( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter & )
{
    switch( ea.getKey() )
    {
        case Producer::KeyChar_Control_L:
        case Producer::KeyChar_Control_R:
            _ctrl = false;
            _decelerateOffsetRate = true;
            _straightenOffset = false;
            break;

        case Producer::KeyChar_Shift_L:
        case Producer::KeyChar_Shift_R:
            _shift = false;
            _decelerateUpSideRate = true;
            break;
    }
}

void UFOManipulator::_keyDown( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
    switch( ea.getKey() )
    {
        case Producer::KeyChar_Control_L:
        case Producer::KeyChar_Control_R:
            _ctrl = true;
            break;

        case Producer::KeyChar_Shift_L :
        case Producer::KeyChar_Shift_R :
            _shift = true;
            break;

        case Producer::KeyChar_Up:
            if( _ctrl )
            {
                _pitchOffsetRate -= _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if( _shift )
                {
                    _upSpeed += _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _forwardSpeed += _speedAccelerationFactor;
            }
            break;

        case Producer::KeyChar_Down:
            if( _ctrl )
            {
                _pitchOffsetRate += _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if( _shift )
                {
                    _upSpeed -= _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _forwardSpeed -= _speedAccelerationFactor;
            }
            break;

        case Producer::KeyChar_Right:
            if( _ctrl )
            {
                _yawOffsetRate += _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if(_shift)
                {
                    _sideSpeed += _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _directionRotationRate -= _directionRotationAcceleration;
            }
            break;

        case Producer::KeyChar_Left:
            if( _ctrl )
            {
                _yawOffsetRate -= _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if(_shift)
                {
                    _sideSpeed -= _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _directionRotationRate += _directionRotationAcceleration;
            }
            break;

        case Producer::KeyChar_Return:
            if( _ctrl )
            {
                _straightenOffset = true;
            }
            break;

        case ' ':
            if( _shift )
            {
                _stop();
            }
            else
            {
                if( fabs(_forwardSpeed) > 0.0 )
                {
                    _forwardSpeed *= _speedDecelerationFactor;

                    if( fabs(_forwardSpeed ) < _speedEpsilon )
                        _forwardSpeed = 0.0;
                }
                if( fabs(_sideSpeed) > 0.0 )
                {
                    _sideSpeed *= _speedDecelerationFactor;

                    if( fabs( _sideSpeed ) < _speedEpsilon )
                        _sideSpeed = 0.0;
                }

                if( fabs(_upSpeed) > 0.0 )
                {
                    _upSpeed *= _speedDecelerationFactor;

                    if( fabs( _upSpeed ) < _speedEpsilon )
                        _sideSpeed = 0.0;
                }

                if( fabs(_directionRotationRate ) > 0.0 )
                {
                    _directionRotationRate *= _directionRotationDeceleration;
                    if( fabs( _directionRotationRate ) < _directionRotationEpsilon )
                        _directionRotationRate = 0.0;
                }

            }
            break;

        case 'H':
            _home();
            break;
    }
}

void UFOManipulator::_frame( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
    double t1 = ea.time();
    if( _t0 == 0.0 )
    {
        _t0 = ea.time();
        _dt = 0.0;
    }
    else
    {
        _dt = t1 - _t0;
        _t0 = t1;
    }

    if( fabs( _directionRotationRate ) > _directionRotationEpsilon )
    {
        _direction = _direction * osg::Matrix::rotate( _directionRotationRate, osg::Vec3(0,0,1));
    }

    {
        osg::Vec3 _sideVec = _direction * osg::Matrix::rotate( -M_PI*0.5, osg::Vec3(0,0,1));

        _position += ((_direction       * _forwardSpeed) +
                      (_sideVec         * _sideSpeed) +
                      (osg::Vec3(0,0,1) * _upSpeed))
                       * _dt;
    }

    _pitchOffset += _pitchOffsetRate * _dt;
    if( _pitchOffset >= M_PI || _pitchOffset < -M_PI )
        _pitchOffset *= -1;

    _yawOffset   += _yawOffsetRate   * _dt;
    if( _yawOffset >= M_PI || _yawOffset < -M_PI )
        _yawOffset *= -1;

    _offset       = osg::Matrix::rotate( _yawOffset, osg::Vec3(0,1,0),
                                         _pitchOffset, osg::Vec3(1,0,0),
                                         0.0, osg::Vec3(0,0,1));

    _adjustPosition();

    _inverseMatrix.makeLookAt( _position, _position + _direction, osg::Vec3(0,0,1));
    _matrix.invert(_inverseMatrix);

    if( _decelerateUpSideRate )
    {
        _upSpeed   *= 0.98;
        _sideSpeed *= 0.98;
    }

    if( _decelerateOffsetRate )
    {
        _yawOffsetRate   *= 0.98;
        _pitchOffsetRate *= 0.98;
    }

    if( _straightenOffset )
    {
        _pitchOffsetRate = 0.0;
        _yawOffsetRate   = 0.0;
        _pitchOffset *= 0.99;
        _yawOffset *= 0.99;

        if( fabs(_pitchOffset ) < 0.01 )
            _pitchOffset = 0.0;
        if( fabs(_yawOffset ) < 0.01 )
            _pitchOffset = 0.0;

        if( _pitchOffset == 0.0 && _yawOffset == 0.0 )
            _straightenOffset = false;
    }
}

void UFOManipulator::_adjustPosition()
{
    if( !_node.valid() )
        return;

    osgUtil::IntersectVisitor iv;

    // Forward line segment at 3 times our intersect distance
    osg::ref_ptr<osg::LineSegment> segForward = new osg::LineSegment;
    segForward->set(_position, _position + (_direction * (_minDistanceInFront * 3.0)) );
    iv.addLineSegment( segForward.get() );

    // Down line segment at 3 times our intersect distance
    osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
    segDown->set(   _position,
                    _position - (osg::Vec3(0,0, _minHeightAboveGround*3)));
    iv.addLineSegment( segDown.get() );

    _node->accept(iv);

    if (iv.hits())
    {
        // Check intersects infront.
        {
            osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segForward.get());
            if (!hitList.empty())
            {
                osg::Vec3d ip = hitList.front().getWorldIntersectPoint();

                double d = (ip - _position).length();

                if( d < _minDistanceInFront )
                {
                    osg::Vec3 op = _position;
                    _position = ip + (_direction * -_minDistanceInFront);
                    _stop();
                }
            }
        }

        // Check intersects below.
        {
            osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
            if (!hitList.empty())
            {
                osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
                if( _position[2] - ip[2] < _minHeightAboveGround )
                    _position[2] = ip[2] + _minHeightAboveGround;
            }
        }

    }
}

void UFOManipulator::_stop()
{
    _forwardSpeed = 0.0;
    _sideSpeed = 0.0;
    _upSpeed = 0.0;
    _directionRotationRate = 0.0;
}
<commit_msg>Added the 'H' - home to usage message<commit_after>#include <osgGA/UFOManipulator>
#include <osgUtil/IntersectVisitor>

using namespace osgGA;

UFOManipulator::UFOManipulator():
            _t0(0.0),
            _shift(false),
            _ctrl(false)
{
    _minHeightAboveGround          = 2.0;
    _minDistanceInFront            = 5.0;

    _speedAccelerationFactor       = 0.4;
    _speedDecelerationFactor       = 0.90;

    _directionRotationRate         = 0.0;
    _directionRotationAcceleration = M_PI*0.00005;
    _directionRotationDeceleration = 0.90;

    _speedEpsilon                  = 0.02;
    _directionRotationEpsilon      = 0.0001;

    _viewOffsetDelta = M_PI * 0.0025;
    _pitchOffsetRate = 0.0;
    _pitchOffset = 0.0;

    _yawOffsetRate = 0.0;
    _yawOffset = 0.0;
    _offset.makeIdentity();

    _decelerateOffsetRate = true;
    _straightenOffset = false;

    _direction.set( 0,1,0);
    _stop();
}

void UFOManipulator::setNode( osg::Node *node )
{
    _node = node;

    if (getAutoComputeHomePosition())
        computeHomePosition();

    _home();
}

const osg::Node* UFOManipulator::getNode() const
{
    return _node.get();
}

osg::Node* UFOManipulator::getNode()
{
    return _node.get();
}

const char* UFOManipulator::className() const
{
    return "UFO";
}

void UFOManipulator::setByMatrix( const osg::Matrix &mat )
{
    _inverseMatrix = mat;
    _matrix.invert( _inverseMatrix );
}

void UFOManipulator::setByInverseMatrix( const osg::Matrix &invmat)
{
    _matrix = invmat;
    _inverseMatrix.invert( _matrix );
}

osg::Matrix UFOManipulator::getMatrix() const
{
        return (_offset * _matrix);
}

osg::Matrix UFOManipulator::getInverseMatrix() const
{
    return (_inverseMatrix * _offset);
}

void UFOManipulator::computeHomePosition()
{
    if( !_node.valid() )
        return;

    osg::BoundingSphere bs = _node->getBound();

    /*
       * Find the ground - Assumption: The ground is the hit of an intersection
       * from a line segment extending from above to below the database at its
       * horizontal center, that intersects the database closest to zero. */
    osgUtil::IntersectVisitor iv;
    osg::ref_ptr<osg::LineSegment> seg = new osg::LineSegment;
    osg::Vec3 A = bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2));
    osg::Vec3 B = bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2));

    if( (B-A).length() == 0.0)
    {
        puts( "DOH" ); fflush(stdout);
        return;
    }

    /*
    seg->set( bs.center() + (osg::Vec3(0,0,1)*(bs.radius()*2)),
              bs.center() + (osg::Vec3(0,0,-1)*(bs.radius()*2)) );
              */
    seg->set( A, B );

    iv.addLineSegment( seg.get() );
    _node->accept(iv);

    // start with it high
    double ground = bs.radius() * 3;

    if (iv.hits())
    {
        osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(seg.get());
        for(osgUtil::IntersectVisitor::HitList::iterator hitr=hitList.begin();
            hitr!=hitList.end(); ++hitr)
        {
            osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
            if( fabs(ip[3]) < ground )
                ground = ip[3];
        }
    }
    else
    {
        osg::notify(osg::WARN)<<"UFOManipulator : I can't find the ground!"<<std::endl;
        ground = 0.0;
    }

    osg::Vec3 p(bs.center()[0], bs.center()[1], ground + (_minHeightAboveGround*1.25) );
    setHomePosition( p, p + osg::Vec3(0,1,0), osg::Vec3(0,0,1) );
}

void UFOManipulator::home(const osgGA::GUIEventAdapter&, osgGA::GUIActionAdapter&)
{
    _home();
}

void UFOManipulator::_home()
{
    if (getAutoComputeHomePosition())
        computeHomePosition();

    _position = _homeEye;
    _direction = _homeCenter - _homeEye;
    _direction.normalize();
    _directionRotationRate = 0.0;

    _inverseMatrix.makeLookAt( _homeEye, _homeCenter, _homeUp );
    _matrix.invert( _matrix );

    _offset.makeIdentity();

    _forwardSpeed = 0.0;
    _sideSpeed = 0.0;
    _upSpeed = 0.0;
}

bool UFOManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter &aa)
{

    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::KEYUP):
            _keyUp( ea, aa );
            return false;
            break;

        case(osgGA::GUIEventAdapter::KEYDOWN):
            _keyDown(ea, aa);
            return false;
            break;

        case(osgGA::GUIEventAdapter::FRAME):
            _frame(ea,aa);
            return false;
            break;

        default:
            return false;
    }

    return false;
}

void UFOManipulator::getUsage(osg::ApplicationUsage& usage) const
{
    usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>",        "Reset the viewing angle to 0.0");
    usage.addKeyboardMouseBinding("UFO Manipulator: <UpArrow>",         "Acceleration forward.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <DownArrow>",       "Acceleration backward (or deceleration forward");
    usage.addKeyboardMouseBinding("UFO Manipulator: <LeftArrow>",       "Rotate view and direction of travel to the left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <RightArrow>",      "Rotate view and direction of travel to the right.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <SpaceBar>",        "Brake.  Gradually decelerates linear and rotational movement.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/UpArrow>",   "Accelerate up.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/DownArrow>", "Accelerate down.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/LeftArrow>", "Accelerate (linearly) left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/RightArrow>","Accelerate (linearly) right.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Shift/SpaceBar>",  "Instant brake.  Immediately stop all linear and rotational movement.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/UpArrow>",    "Rotate view (but not direction of travel) up.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/DownArrow>",  "Rotate view (but not direction of travel) down.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/LeftArrow>",  "Rotate view (but not direction of travel) left.");
    usage.addKeyboardMouseBinding("UFO Manipulator: <Ctrl/RightArrow>", "Rotate view (but not direction of travel) right.");
    usage.addKeyboardMouseBinding("UFO Manipulator: 'H'", "go to Home position.");
}

void UFOManipulator::_keyUp( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter & )
{
    switch( ea.getKey() )
    {
        case Producer::KeyChar_Control_L:
        case Producer::KeyChar_Control_R:
            _ctrl = false;
            _decelerateOffsetRate = true;
            _straightenOffset = false;
            break;

        case Producer::KeyChar_Shift_L:
        case Producer::KeyChar_Shift_R:
            _shift = false;
            _decelerateUpSideRate = true;
            break;
    }
}

void UFOManipulator::_keyDown( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
    switch( ea.getKey() )
    {
        case Producer::KeyChar_Control_L:
        case Producer::KeyChar_Control_R:
            _ctrl = true;
            break;

        case Producer::KeyChar_Shift_L :
        case Producer::KeyChar_Shift_R :
            _shift = true;
            break;

        case Producer::KeyChar_Up:
            if( _ctrl )
            {
                _pitchOffsetRate -= _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if( _shift )
                {
                    _upSpeed += _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _forwardSpeed += _speedAccelerationFactor;
            }
            break;

        case Producer::KeyChar_Down:
            if( _ctrl )
            {
                _pitchOffsetRate += _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if( _shift )
                {
                    _upSpeed -= _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _forwardSpeed -= _speedAccelerationFactor;
            }
            break;

        case Producer::KeyChar_Right:
            if( _ctrl )
            {
                _yawOffsetRate += _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if(_shift)
                {
                    _sideSpeed += _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _directionRotationRate -= _directionRotationAcceleration;
            }
            break;

        case Producer::KeyChar_Left:
            if( _ctrl )
            {
                _yawOffsetRate -= _viewOffsetDelta;
                _decelerateOffsetRate = false;
            }
            else
            {
                if(_shift)
                {
                    _sideSpeed -= _speedAccelerationFactor;
                    _decelerateUpSideRate = false;
                }
                else
                    _directionRotationRate += _directionRotationAcceleration;
            }
            break;

        case Producer::KeyChar_Return:
            if( _ctrl )
            {
                _straightenOffset = true;
            }
            break;

        case ' ':
            if( _shift )
            {
                _stop();
            }
            else
            {
                if( fabs(_forwardSpeed) > 0.0 )
                {
                    _forwardSpeed *= _speedDecelerationFactor;

                    if( fabs(_forwardSpeed ) < _speedEpsilon )
                        _forwardSpeed = 0.0;
                }
                if( fabs(_sideSpeed) > 0.0 )
                {
                    _sideSpeed *= _speedDecelerationFactor;

                    if( fabs( _sideSpeed ) < _speedEpsilon )
                        _sideSpeed = 0.0;
                }

                if( fabs(_upSpeed) > 0.0 )
                {
                    _upSpeed *= _speedDecelerationFactor;

                    if( fabs( _upSpeed ) < _speedEpsilon )
                        _sideSpeed = 0.0;
                }

                if( fabs(_directionRotationRate ) > 0.0 )
                {
                    _directionRotationRate *= _directionRotationDeceleration;
                    if( fabs( _directionRotationRate ) < _directionRotationEpsilon )
                        _directionRotationRate = 0.0;
                }

            }
            break;

        case 'H':
            _home();
            break;
    }
}

void UFOManipulator::_frame( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter & )
{
    double t1 = ea.time();
    if( _t0 == 0.0 )
    {
        _t0 = ea.time();
        _dt = 0.0;
    }
    else
    {
        _dt = t1 - _t0;
        _t0 = t1;
    }

    if( fabs( _directionRotationRate ) > _directionRotationEpsilon )
    {
        _direction = _direction * osg::Matrix::rotate( _directionRotationRate, osg::Vec3(0,0,1));
    }

    {
        osg::Vec3 _sideVec = _direction * osg::Matrix::rotate( -M_PI*0.5, osg::Vec3(0,0,1));

        _position += ((_direction       * _forwardSpeed) +
                      (_sideVec         * _sideSpeed) +
                      (osg::Vec3(0,0,1) * _upSpeed))
                       * _dt;
    }

    _pitchOffset += _pitchOffsetRate * _dt;
    if( _pitchOffset >= M_PI || _pitchOffset < -M_PI )
        _pitchOffset *= -1;

    _yawOffset   += _yawOffsetRate   * _dt;
    if( _yawOffset >= M_PI || _yawOffset < -M_PI )
        _yawOffset *= -1;

    _offset       = osg::Matrix::rotate( _yawOffset, osg::Vec3(0,1,0),
                                         _pitchOffset, osg::Vec3(1,0,0),
                                         0.0, osg::Vec3(0,0,1));

    _adjustPosition();

    _inverseMatrix.makeLookAt( _position, _position + _direction, osg::Vec3(0,0,1));
    _matrix.invert(_inverseMatrix);

    if( _decelerateUpSideRate )
    {
        _upSpeed   *= 0.98;
        _sideSpeed *= 0.98;
    }

    if( _decelerateOffsetRate )
    {
        _yawOffsetRate   *= 0.98;
        _pitchOffsetRate *= 0.98;
    }

    if( _straightenOffset )
    {
        _pitchOffsetRate = 0.0;
        _yawOffsetRate   = 0.0;
        _pitchOffset *= 0.99;
        _yawOffset *= 0.99;

        if( fabs(_pitchOffset ) < 0.01 )
            _pitchOffset = 0.0;
        if( fabs(_yawOffset ) < 0.01 )
            _pitchOffset = 0.0;

        if( _pitchOffset == 0.0 && _yawOffset == 0.0 )
            _straightenOffset = false;
    }
}

void UFOManipulator::_adjustPosition()
{
    if( !_node.valid() )
        return;

    osgUtil::IntersectVisitor iv;

    // Forward line segment at 3 times our intersect distance
    osg::ref_ptr<osg::LineSegment> segForward = new osg::LineSegment;
    segForward->set(_position, _position + (_direction * (_minDistanceInFront * 3.0)) );
    iv.addLineSegment( segForward.get() );

    // Down line segment at 3 times our intersect distance
    osg::ref_ptr<osg::LineSegment> segDown = new osg::LineSegment;
    segDown->set(   _position,
                    _position - (osg::Vec3(0,0, _minHeightAboveGround*3)));
    iv.addLineSegment( segDown.get() );

    _node->accept(iv);

    if (iv.hits())
    {
        // Check intersects infront.
        {
            osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segForward.get());
            if (!hitList.empty())
            {
                osg::Vec3d ip = hitList.front().getWorldIntersectPoint();

                double d = (ip - _position).length();

                if( d < _minDistanceInFront )
                {
                    osg::Vec3 op = _position;
                    _position = ip + (_direction * -_minDistanceInFront);
                    _stop();
                }
            }
        }

        // Check intersects below.
        {
            osgUtil::IntersectVisitor::HitList& hitList = iv.getHitList(segDown.get());
            if (!hitList.empty())
            {
                osg::Vec3d ip = hitList.front().getWorldIntersectPoint();
                if( _position[2] - ip[2] < _minHeightAboveGround )
                    _position[2] = ip[2] + _minHeightAboveGround;
            }
        }

    }
}

void UFOManipulator::_stop()
{
    _forwardSpeed = 0.0;
    _sideSpeed = 0.0;
    _upSpeed = 0.0;
    _directionRotationRate = 0.0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -d Print debugging information (-dd shows more)
    -h Print this help and exit
    -l Print the list of handled USDTs and exit
    -s RESPONSE_HEADER_NAMES Response header names to show, e.g. "content-type"
    -t EVENT_TYPES Fully-qualified probe names to show, e.g. "quicly:accept"
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept -t quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header -t h2o:h3s_accept -t h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    int debug = 0;
    int list_and_exit = 0;
    FILE *outfp = stdout;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:l")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            event_type_filters.push_back(optarg);
            break;
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'l':
            list_and_exit++;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (list_and_exit) {
        for (const auto &usdt : tracer->usdt_probes()) {
            fprintf(stderr, "%s:%s\n", usdt.provider.c_str(), usdt.name.c_str());
        }
        exit(EXIT_SUCCESS);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (debug >= 2) {
        fprintf(stderr, "event_type_filters=");
        for (size_t i = 0; i < event_type_filters.size(); i++) {
            if (i > 0) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", event_type_filters[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;
    for (const auto &probe_def : tracer->usdt_probes()) {
        if (event_type_filters.empty() || std::find(event_type_filters.cbegin(), event_type_filters.cend(),
                                                    probe_def.provider + ":" + probe_def.name) != event_type_filters.cend()) {
            probes.push_back(ebpf::USDT(h2o_pid, probe_def.provider, probe_def.name, probe_def.probe_func));
        }
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<commit_msg>h2olog: use bool for boolean<commit_after>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -d Print debugging information (-dd shows more)
    -h Print this help and exit
    -l Print the list of handled USDTs and exit
    -s RESPONSE_HEADER_NAMES Response header names to show, e.g. "content-type"
    -t EVENT_TYPES Fully-qualified probe names to show, e.g. "quicly:accept"
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept -t quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header -t h2o:h3s_accept -t h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    int debug = 0;
    bool list_and_exit = false;
    FILE *outfp = stdout;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:l")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            event_type_filters.push_back(optarg);
            break;
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'l':
            list_and_exit = true;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (list_and_exit) {
        for (const auto &usdt : tracer->usdt_probes()) {
            fprintf(stderr, "%s:%s\n", usdt.provider.c_str(), usdt.name.c_str());
        }
        exit(EXIT_SUCCESS);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (debug >= 2) {
        fprintf(stderr, "event_type_filters=");
        for (size_t i = 0; i < event_type_filters.size(); i++) {
            if (i > 0) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", event_type_filters[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;
    for (const auto &probe_def : tracer->usdt_probes()) {
        if (event_type_filters.empty() || std::find(event_type_filters.cbegin(), event_type_filters.cend(),
                                                    probe_def.provider + ":" + probe_def.name) != event_type_filters.cend()) {
            probes.push_back(ebpf::USDT(h2o_pid, probe_def.provider, probe_def.name, probe_def.probe_func));
        }
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -d Print debugging information (-dd shows more)
    -h Print this help and exit
    -l Print the list of handled USDTs and exit
    -s RESPONSE_HEADER_NAMES Response header names to show, e.g. "content-type"
    -t EVENT_TYPES Fully-qualified probe names to show, e.g. "quicly:accept"
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept -t quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header -t h2o:h3s_accept -t h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    std::vector<h2o_tracer::usdt> available_usdts = tracer->usdt_probes();

    int debug = 0;
    bool list_and_exit = false;
    FILE *outfp = stdout;
    std::vector<h2o_tracer::usdt> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:l")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't': {
            auto found = std::find_if(available_usdts.cbegin(), available_usdts.cend(),
                                      [](const h2o_tracer::usdt &usdt) { return optarg == usdt.fully_qualified_name(); });
            if (found == available_usdts.cend()) {
                fprintf(stderr, "No such event type: %s\n", optarg);
                exit(EXIT_FAILURE);
            }
            event_type_filters.push_back(*found);
            break;
        }
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'l':
            list_and_exit = true;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (list_and_exit) {
        for (const auto &usdt : tracer->usdt_probes()) {
            printf("%s:%s\n", usdt.provider.c_str(), usdt.name.c_str());
        }
        exit(EXIT_SUCCESS);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (event_type_filters.empty()) {
        event_type_filters = available_usdts;
    }

    if (debug >= 2) {
        fprintf(stderr, "event_type_filters=");
        for (auto iter = event_type_filters.cbegin(); iter != event_type_filters.cend(); iter++) {
            if (iter != event_type_filters.cbegin()) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", iter->fully_qualified_name().c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;

    for (const auto &usdt : event_type_filters) {
        probes.push_back(ebpf::USDT(h2o_pid, usdt.provider, usdt.name, usdt.probe_func));
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<commit_msg>h2olog: refine the code<commit_after>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -d Print debugging information (-dd shows more)
    -h Print this help and exit
    -l Print the list of available probe names and exit
    -s RESPONSE_HEADER_NAME A response header name to show, e.g. "content-type"
    -t PROBE_NAME A fully-qualified probe name to show, e.g. "quicly:accept"
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept -t quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header -t h2o:h3s_accept -t h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    const std::vector<h2o_tracer::usdt> available_usdts = tracer->usdt_probes();

    int debug = 0;
    FILE *outfp = stdout;
    std::vector<h2o_tracer::usdt> selected_usdts;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:l")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't': {
            auto found = std::find_if(available_usdts.cbegin(), available_usdts.cend(),
                                      [](const h2o_tracer::usdt &usdt) { return optarg == usdt.fully_qualified_name(); });
            if (found == available_usdts.cend()) {
                fprintf(stderr, "No such event type: %s\n", optarg);
                exit(EXIT_FAILURE);
            }
            selected_usdts.push_back(*found);
            break;
        }
        case 's':
            response_header_filters.push_back(optarg);
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'l':
            for (const auto &usdt : available_usdts) {
                printf("%s\n", usdt.fully_qualified_name().c_str());
            }
            exit(EXIT_SUCCESS);
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (selected_usdts.empty()) {
        selected_usdts = available_usdts;
    }

    if (debug >= 2) {
        fprintf(stderr, "selected_usdts=");
        for (auto iter = selected_usdts.cbegin(); iter != selected_usdts.cend(); iter++) {
            if (iter != selected_usdts.cbegin()) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", iter->fully_qualified_name().c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;

    for (const auto &usdt : selected_usdts) {
        probes.push_back(ebpf::USDT(h2o_pid, usdt.provider, usdt.name, usdt.probe_func));
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>// Copyright 2009-2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "v8.h"

#include "heap-profiler.h"
#include "profile-generator.h"

namespace v8 {
namespace internal {

HeapProfiler::HeapProfiler()
    : snapshots_(new HeapSnapshotsCollection()),
      next_snapshot_uid_(1) {
}

HeapProfiler::~HeapProfiler() {
  delete snapshots_;
}

void HeapProfiler::ResetSnapshots() {
  delete snapshots_;
  snapshots_ = new HeapSnapshotsCollection();
}

void HeapProfiler::SetUp() {
  Isolate* isolate = Isolate::Current();
  if (isolate->heap_profiler() == NULL) {
    isolate->set_heap_profiler(new HeapProfiler());
  }
}

void HeapProfiler::TearDown() {
  Isolate* isolate = Isolate::Current();
  delete isolate->heap_profiler();
  isolate->set_heap_profiler(NULL);
}

HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name,
                                         int type,
                                         v8::ActivityControl* control) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
                                                               type,
                                                               control);
}

HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
                                         int type,
                                         v8::ActivityControl* control) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
                                                               type,
                                                               control);
}

void HeapProfiler::StartHeapObjectsTracking() {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  Isolate::Current()->heap_profiler()->StartHeapObjectsTrackingImpl();
}

void HeapProfiler::StopHeapObjectsTracking() {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  Isolate::Current()->heap_profiler()->StopHeapObjectsTrackingImpl();
}

void HeapProfiler::PushHeapObjectsStats(v8::OutputStream* stream) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->PushHeapObjectsStatsImpl(stream);
}

void HeapProfiler::DefineWrapperClass(
    uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
  ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
  if (wrapper_callbacks_.length() <= class_id) {
    wrapper_callbacks_.AddBlock(
        NULL, class_id - wrapper_callbacks_.length() + 1);
  }
  wrapper_callbacks_[class_id] = callback;
}

v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
    uint16_t class_id, Object** wrapper) {
  if (wrapper_callbacks_.length() <= class_id) return NULL;
  return wrapper_callbacks_[class_id](
      class_id, Utils::ToLocal(Handle<Object>(wrapper)));
}

HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
                                             int type,
                                             v8::ActivityControl* control) {
  HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
  HeapSnapshot* result =
      snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
  bool generation_completed = true;
  switch (s_type) {
    case HeapSnapshot::kFull: {
      HeapSnapshotGenerator generator(result, control);
      generation_completed = generator.GenerateSnapshot();
      break;
    }
    default:
      UNREACHABLE();
  }
  if (!generation_completed) {
    delete result;
    result = NULL;
  }
  snapshots_->SnapshotGenerationFinished(result);
  return result;
}

HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
                                             int type,
                                             v8::ActivityControl* control) {
  return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
}

void HeapProfiler::StartHeapObjectsTrackingImpl() {
  snapshots_->StartHeapObjectsTracking();
}

void HeapProfiler::PushHeapObjectsStatsImpl(OutputStream* stream) {
  snapshots_->PushHeapObjectsStats(stream);
}

void HeapProfiler::StopHeapObjectsTrackingImpl() {
  snapshots_->StopHeapObjectsTracking();
}

int HeapProfiler::GetSnapshotsCount() {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->snapshots()->length();
}

HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->snapshots()->at(index);
}

HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->GetSnapshot(uid);
}

SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
  if (!obj->IsHeapObject())
    return v8::HeapProfiler::kUnknownObjectId;
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->FindObjectId(HeapObject::cast(*obj) ->address());
}

void HeapProfiler::DeleteAllSnapshots() {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  profiler->ResetSnapshots();
}

void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
  snapshots_->ObjectMoveEvent(from, to);
}

} }  // namespace v8::internal
<commit_msg>Remove extra whitespace added in r11339<commit_after>// Copyright 2009-2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "v8.h"

#include "heap-profiler.h"
#include "profile-generator.h"

namespace v8 {
namespace internal {

HeapProfiler::HeapProfiler()
    : snapshots_(new HeapSnapshotsCollection()),
      next_snapshot_uid_(1) {
}

HeapProfiler::~HeapProfiler() {
  delete snapshots_;
}

void HeapProfiler::ResetSnapshots() {
  delete snapshots_;
  snapshots_ = new HeapSnapshotsCollection();
}

void HeapProfiler::SetUp() {
  Isolate* isolate = Isolate::Current();
  if (isolate->heap_profiler() == NULL) {
    isolate->set_heap_profiler(new HeapProfiler());
  }
}

void HeapProfiler::TearDown() {
  Isolate* isolate = Isolate::Current();
  delete isolate->heap_profiler();
  isolate->set_heap_profiler(NULL);
}

HeapSnapshot* HeapProfiler::TakeSnapshot(const char* name,
                                         int type,
                                         v8::ActivityControl* control) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
                                                               type,
                                                               control);
}

HeapSnapshot* HeapProfiler::TakeSnapshot(String* name,
                                         int type,
                                         v8::ActivityControl* control) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->TakeSnapshotImpl(name,
                                                               type,
                                                               control);
}

void HeapProfiler::StartHeapObjectsTracking() {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  Isolate::Current()->heap_profiler()->StartHeapObjectsTrackingImpl();
}

void HeapProfiler::StopHeapObjectsTracking() {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  Isolate::Current()->heap_profiler()->StopHeapObjectsTrackingImpl();
}

void HeapProfiler::PushHeapObjectsStats(v8::OutputStream* stream) {
  ASSERT(Isolate::Current()->heap_profiler() != NULL);
  return Isolate::Current()->heap_profiler()->PushHeapObjectsStatsImpl(stream);
}

void HeapProfiler::DefineWrapperClass(
    uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
  ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
  if (wrapper_callbacks_.length() <= class_id) {
    wrapper_callbacks_.AddBlock(
        NULL, class_id - wrapper_callbacks_.length() + 1);
  }
  wrapper_callbacks_[class_id] = callback;
}

v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
    uint16_t class_id, Object** wrapper) {
  if (wrapper_callbacks_.length() <= class_id) return NULL;
  return wrapper_callbacks_[class_id](
      class_id, Utils::ToLocal(Handle<Object>(wrapper)));
}

HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
                                             int type,
                                             v8::ActivityControl* control) {
  HeapSnapshot::Type s_type = static_cast<HeapSnapshot::Type>(type);
  HeapSnapshot* result =
      snapshots_->NewSnapshot(s_type, name, next_snapshot_uid_++);
  bool generation_completed = true;
  switch (s_type) {
    case HeapSnapshot::kFull: {
      HeapSnapshotGenerator generator(result, control);
      generation_completed = generator.GenerateSnapshot();
      break;
    }
    default:
      UNREACHABLE();
  }
  if (!generation_completed) {
    delete result;
    result = NULL;
  }
  snapshots_->SnapshotGenerationFinished(result);
  return result;
}

HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
                                             int type,
                                             v8::ActivityControl* control) {
  return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
}

void HeapProfiler::StartHeapObjectsTrackingImpl() {
  snapshots_->StartHeapObjectsTracking();
}

void HeapProfiler::PushHeapObjectsStatsImpl(OutputStream* stream) {
  snapshots_->PushHeapObjectsStats(stream);
}

void HeapProfiler::StopHeapObjectsTrackingImpl() {
  snapshots_->StopHeapObjectsTracking();
}

int HeapProfiler::GetSnapshotsCount() {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->snapshots()->length();
}

HeapSnapshot* HeapProfiler::GetSnapshot(int index) {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->snapshots()->at(index);
}

HeapSnapshot* HeapProfiler::FindSnapshot(unsigned uid) {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->GetSnapshot(uid);
}

SnapshotObjectId HeapProfiler::GetSnapshotObjectId(Handle<Object> obj) {
  if (!obj->IsHeapObject())
    return v8::HeapProfiler::kUnknownObjectId;
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  return profiler->snapshots_->FindObjectId(HeapObject::cast(*obj)->address());
}

void HeapProfiler::DeleteAllSnapshots() {
  HeapProfiler* profiler = Isolate::Current()->heap_profiler();
  ASSERT(profiler != NULL);
  profiler->ResetSnapshots();
}

void HeapProfiler::ObjectMoveEvent(Address from, Address to) {
  snapshots_->ObjectMoveEvent(from, to);
}

} }  // namespace v8::internal
<|endoftext|>