<commit_before>// fastmap.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include "plugin_utils.h"
#include <map>
#include <string>

BZ_GET_PLUGIN_VERSION

class FastMapEventHandler;

class FastMapClient : public bz_NonPlayerConnectionHandler
{
public:
  FastMapClient( int connectionID = -1, FastMapEventHandler *h = NULL);
  virtual ~FastMapClient();
  virtual void pending ( int connectionID, void *d, unsigned int s );
  virtual void disconnect ( int connectionID );

  void startTransfer ( unsigned char * d, unsigned int s );
  bool updateTransfer ( void );

  int conID;

  unsigned char *data;
  unsigned int size;
  unsigned int currerntPos;

  std::string command;

  FastMapEventHandler* handler;

  std::vector<double> updatTimes;

  unsigned int dataSent;
};

class FastMapEventHandler : public bz_EventHandler
{
public:
  FastMapEventHandler();
  virtual ~FastMapEventHandler();

  virtual void process ( bz_EventData *eventData );

  void updateHTTPServer ( void );
  void killHTTPServer ( void );

  std::string	mapName;

  std::map<int,FastMapClient*> clients;

  unsigned char* mapData;
  unsigned int mapDataSize;
};

FastMapEventHandler fastMapEventHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* /*commandLine*/ )
{
  bz_debugMessage(4,"fastmap plugin loaded");
  bz_registerEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_registerEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
  fastMapEventHandler.killHTTPServer();

  bz_debugMessage(4,"fastmap plugin unloaded");
  bz_removeEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_removeEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

FastMapEventHandler::FastMapEventHandler()
{
  mapName = format("PrivateMap%d",(unsigned int)this);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapEventHandler::~FastMapEventHandler()
{
  killHTTPServer();
}

void FastMapEventHandler::process ( bz_EventData *eventData )
{
  if ( eventData->eventType == bz_eWorldFinalized )
  {
    killHTTPServer();

    if (!bz_getPublic() || bz_getClientWorldDowloadURL().size())
      return;

    mapDataSize = bz_getWorldCacheSize();

    mapData = (unsigned char*)malloc(mapDataSize);
    if (!mapDataSize)
      return;

    bz_getWorldCacheData(mapData);

    mapName = format("%d",(unsigned int)this);

    std::string hostport = "127.0.0.1:5154";
    if (bz_getPublicAddr().size())
      hostport = bz_getPublicAddr().c_str();

    // make sure it has the port
    if ( strrchr(hostport.c_str(),':') == NULL )
      hostport += format(":%d",bz_getPublicPort());

    std::string url = format("HTTP://%s/%s",hostport.c_str(),mapName.c_str());

    bz_debugMessagef(2,"FastMap: Runing local HTTP server for maps using URL %s",url.c_str());
    bz_setClientWorldDowloadURL(url.c_str());
  }
  else if ( eventData->eventType == bz_eTickEvent)
  {
    if ( mapDataSize )
      updateHTTPServer();
  }
  else if ( eventData->eventType == bz_eNewNonPlayerConnection )
  {
    if ( mapDataSize )
    {
      bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;
      if (connData->size >= 3 && strncmp((const char*)connData->data,"GET",3) == 0)
      {
	FastMapClient * handler = new FastMapClient(connData->connectionID,this);

	if(bz_registerNonPlayerConnectionHandler ( connData->connectionID, handler ) )
	{
	  bz_debugMessagef(2,"FastMap: Local HTTP connection from %d",connData->connectionID);
	  clients[connData->connectionID] = handler;
	  handler->pending (connData->connectionID,connData->data, connData->size);
	}
	else
	  delete(handler);
      }
    }
  }
  else if ( eventData->eventType == bz_eIdleNewNonPlayerConnection )
  {
    bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;

    std::map<int,FastMapClient*>::iterator itr = clients.find(connData->connectionID);
    if( itr != clients.end() )
    {
      bz_removeNonPlayerConnectionHandler ( itr->second->conID, itr->second );
      delete(itr->second);
      clients.erase(itr);
    }
    bz_disconectNonPlayerConnection ( connData->connectionID );
 }
}

void FastMapEventHandler::updateHTTPServer ( void )
{
  std::vector<int> killList;

  std::map<int,FastMapClient*>::iterator itr = clients.begin();
  while (itr != clients.end())
  {
    if(itr->second->conID == -1 || !itr->second->updateTransfer() )
      killList.push_back(itr->first);
    itr++;
  }

  for ( unsigned int i = 0; i < (unsigned int)killList.size(); i++)
  {
    itr = clients.find(killList[i]);
    FastMapClient *client = itr->second;
    if ( bz_getDebugLevel() >= 2 && client->updatTimes.size()>1)
    {
      double totalTime = (*(client->updatTimes.end()-1)) - (*(client->updatTimes.begin()));
      double averageWait;
      double maxWait = 0;
      double minWait = 99999999.0;

      for (unsigned int k = 0; k < (unsigned int)client->updatTimes.size(); k++)
      {
	if ( k > 1 )
	{
	  double wait = client->updatTimes[k] - client->updatTimes[k-1];
	  averageWait + wait;
	  if ( wait > maxWait)
	    maxWait = wait;
	  if ( wait < minWait)
	    minWait = wait;
	}
      }
      averageWait /= (double)client->updatTimes.size()-1;

      bz_debugMessagef(2,"FastMap: File Transfer Complete");
      bz_debugMessagef(2,"FastMap: File Transfer Stats");
      bz_debugMessagef(2,"FastMap:  %d bytes transfered in %f seconds (%fbps)",client->dataSent,totalTime,(double)client->dataSent/totalTime);
      bz_debugMessagef(3,"FastMap:  %f average wait",averageWait);
      bz_debugMessagef(3,"FastMap:  %f max wait",maxWait);
      bz_debugMessagef(3,"FastMap:  %f min wait",minWait);
    }
    clients.erase(itr);
  }

  if (clients.size())
    bz_setMaxWaitTime(0.001f);
  else
    bz_setMaxWaitTime(2.0f);
}

void FastMapEventHandler::killHTTPServer ( void )
{
  if ( mapData )
    free(mapData);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapClient::FastMapClient( int connectionID, FastMapEventHandler *h )
{
  conID = connectionID;

  data = NULL;
  size = 0;
  currerntPos = 0;
  handler = h;
  dataSent = 0;
}

FastMapClient::~FastMapClient()
{
  if (data)
    free(data);
}

void FastMapClient::pending ( int connectionID, void *s, unsigned int d )
{
  if ( connectionID != conID )
    return;

  char *c = (char*)malloc(d+1);
  memcpy(c,s,d);
  c[d] = 0;
  command += c;
  free (c);

  // parse the command
  std::vector<std::string> commandList = tokenize(command,std::string("\r\n"),0,false);

  if ( commandList.size() == 1 && commandList[0].size() == command.size() ) // there were no delims, we are done.
    return;

  for (unsigned int i = 0; i < (unsigned int)commandList.size();i++)
  {
    command.erase(command.begin(),command.begin()+commandList[i].size());

    std::string thisLine = commandList[i];

    if (strncmp(thisLine.c_str(),"GET",3) == 0 && !data) // it's a get and we arn't in a transfer
    {
      std::vector<std::string> params = tokenize(thisLine,std::string(" "),0,false);
      if ( params.size() <= 3 )
      {
	std::string urlPath = params[1];

	if (urlPath.size()>2 && handler->mapName == urlPath.c_str()+1 )
	{
	  startTransfer(handler->mapData,handler->mapDataSize);
	}
      }
    }
  }
}

void FastMapClient::disconnect ( int connectionID )
{
  if (connectionID != conID)
    return;

  bz_removeNonPlayerConnectionHandler ( conID, this );
  bz_disconectNonPlayerConnection ( conID );
  conID = -1;

}
void FastMapClient::startTransfer ( unsigned char * d, unsigned int s )
{
  if (data)
    free (data);
  data = NULL;
  size = 0;
  currerntPos = 0;

  if (!s)
    return;

  bz_debugMessagef(2,"FastMap: File Transfer for connection %d started",conID);

  data = (unsigned char*)malloc(s);
  memcpy(data,d,s);
  size = s;

  std::string httpHeaders;
  httpHeaders += "HTTP/1.1 200 OK\n";
  httpHeaders += format("Content-Length: %d\n", size);
  httpHeaders += "Connection: close\n";
  httpHeaders += "Content-Type: application/octet-stream\n";
  httpHeaders += "\n";

  dataSent += (unsigned int)httpHeaders.size();
  if (!bz_sendNonPlayerData ( conID, httpHeaders.c_str(), (unsigned int)httpHeaders.size() ) || !updateTransfer())
    disconnect(conID);
}

bool FastMapClient::updateTransfer ( void )
{
  int chunkToSend = 1000;

  if ( currerntPos >= size )
    return false;

  updatTimes.push_back(bz_getCurrentTime());
  // wait till the current data is sent
  if (bz_getNonPlayerConnectionOutboundPacketCount(conID) != 0)
    return true;

  if ( currerntPos + chunkToSend > size)
    chunkToSend = size-currerntPos;

  bool worked = bz_sendNonPlayerData ( conID, data+currerntPos, chunkToSend );

  currerntPos += chunkToSend;
  dataSent += chunkToSend;

  return worked;
}

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>lot HTTP gets to level 2, others to 3<commit_after>// fastmap.cpp : Defines the entry point for the DLL application.
//

#include "bzfsAPI.h"
#include "plugin_utils.h"
#include <map>
#include <string>

BZ_GET_PLUGIN_VERSION

class FastMapEventHandler;

class FastMapClient : public bz_NonPlayerConnectionHandler
{
public:
  FastMapClient( int connectionID = -1, FastMapEventHandler *h = NULL);
  virtual ~FastMapClient();
  virtual void pending ( int connectionID, void *d, unsigned int s );
  virtual void disconnect ( int connectionID );

  void startTransfer ( unsigned char * d, unsigned int s );
  bool updateTransfer ( void );

  int conID;

  unsigned char *data;
  unsigned int size;
  unsigned int currerntPos;

  std::string command;

  FastMapEventHandler* handler;

  std::vector<double> updatTimes;

  unsigned int dataSent;
};

class FastMapEventHandler : public bz_EventHandler
{
public:
  FastMapEventHandler();
  virtual ~FastMapEventHandler();

  virtual void process ( bz_EventData *eventData );

  void updateHTTPServer ( void );
  void killHTTPServer ( void );

  std::string	mapName;

  std::map<int,FastMapClient*> clients;

  unsigned char* mapData;
  unsigned int mapDataSize;
};

FastMapEventHandler fastMapEventHandler;

BZF_PLUGIN_CALL int bz_Load ( const char* /*commandLine*/ )
{
  bz_debugMessage(4,"fastmap plugin loaded");
  bz_registerEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_registerEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_registerEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

BZF_PLUGIN_CALL int bz_Unload ( void )
{
  fastMapEventHandler.killHTTPServer();

  bz_debugMessage(4,"fastmap plugin unloaded");
  bz_removeEvent (bz_eWorldFinalized,&fastMapEventHandler);
  bz_removeEvent (bz_eTickEvent,&fastMapEventHandler);
  bz_removeEvent (bz_eNewNonPlayerConnection,&fastMapEventHandler);
return 0;
}

FastMapEventHandler::FastMapEventHandler()
{
  mapName = format("PrivateMap%d",(unsigned int)this);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapEventHandler::~FastMapEventHandler()
{
  killHTTPServer();
}

void FastMapEventHandler::process ( bz_EventData *eventData )
{
  if ( eventData->eventType == bz_eWorldFinalized )
  {
    killHTTPServer();

    if (!bz_getPublic() || bz_getClientWorldDowloadURL().size())
      return;

    mapDataSize = bz_getWorldCacheSize();

    mapData = (unsigned char*)malloc(mapDataSize);
    if (!mapDataSize)
      return;

    bz_getWorldCacheData(mapData);

    mapName = format("%d",(unsigned int)this);

    std::string hostport = "127.0.0.1:5154";
    if (bz_getPublicAddr().size())
      hostport = bz_getPublicAddr().c_str();

    // make sure it has the port
    if ( strrchr(hostport.c_str(),':') == NULL )
      hostport += format(":%d",bz_getPublicPort());

    std::string url = format("HTTP://%s/%s",hostport.c_str(),mapName.c_str());

    bz_debugMessagef(2,"FastMap: Runing local HTTP server for maps using URL %s",url.c_str());
    bz_setClientWorldDowloadURL(url.c_str());
  }
  else if ( eventData->eventType == bz_eTickEvent)
  {
    if ( mapDataSize )
      updateHTTPServer();
  }
  else if ( eventData->eventType == bz_eNewNonPlayerConnection )
  {
    if ( mapDataSize )
    {
      bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;
      if (connData->size >= 3 && strncmp((const char*)connData->data,"GET",3) == 0)
      {
	FastMapClient * handler = new FastMapClient(connData->connectionID,this);

	if(bz_registerNonPlayerConnectionHandler ( connData->connectionID, handler ) )
	{
	  bz_debugMessagef(2,"FastMap: Local HTTP connection from %d",connData->connectionID);
	  clients[connData->connectionID] = handler;
	  handler->pending (connData->connectionID,connData->data, connData->size);
	}
	else
	  delete(handler);
      }
    }
  }
  else if ( eventData->eventType == bz_eIdleNewNonPlayerConnection )
  {
    bz_NewNonPlayerConnectionEventData_V1 *connData = (bz_NewNonPlayerConnectionEventData_V1*)eventData;

    std::map<int,FastMapClient*>::iterator itr = clients.find(connData->connectionID);
    if( itr != clients.end() )
    {
      bz_removeNonPlayerConnectionHandler ( itr->second->conID, itr->second );
      delete(itr->second);
      clients.erase(itr);
    }
    bz_disconectNonPlayerConnection ( connData->connectionID );
 }
}

void FastMapEventHandler::updateHTTPServer ( void )
{
  std::vector<int> killList;

  std::map<int,FastMapClient*>::iterator itr = clients.begin();
  while (itr != clients.end())
  {
    if(itr->second->conID == -1 || !itr->second->updateTransfer() )
      killList.push_back(itr->first);
    itr++;
  }

  for ( unsigned int i = 0; i < (unsigned int)killList.size(); i++)
  {
    itr = clients.find(killList[i]);
    FastMapClient *client = itr->second;
    if ( bz_getDebugLevel() >= 2 && client->updatTimes.size()>1)
    {
      double totalTime = (*(client->updatTimes.end()-1)) - (*(client->updatTimes.begin()));
      double averageWait;
      double maxWait = 0;
      double minWait = 99999999.0;

      for (unsigned int k = 0; k < (unsigned int)client->updatTimes.size(); k++)
      {
	if ( k > 1 )
	{
	  double wait = client->updatTimes[k] - client->updatTimes[k-1];
	  averageWait + wait;
	  if ( wait > maxWait)
	    maxWait = wait;
	  if ( wait < minWait)
	    minWait = wait;
	}
      }
      averageWait /= (double)client->updatTimes.size()-1;

      bz_debugMessagef(2,"FastMap: File Transfer Complete");
      bz_debugMessagef(2,"FastMap: File Transfer Stats");
      bz_debugMessagef(2,"FastMap:  %d bytes transfered in %f seconds (%fbps)",client->dataSent,totalTime,(double)client->dataSent/totalTime);
      bz_debugMessagef(3,"FastMap:  %f average wait",averageWait);
      bz_debugMessagef(3,"FastMap:  %f max wait",maxWait);
      bz_debugMessagef(3,"FastMap:  %f min wait",minWait);
    }
    clients.erase(itr);
  }

  if (clients.size())
    bz_setMaxWaitTime(0.001f);
  else
    bz_setMaxWaitTime(2.0f);
}

void FastMapEventHandler::killHTTPServer ( void )
{
  if ( mapData )
    free(mapData);

  mapData = NULL;
  mapDataSize = 0;
}

FastMapClient::FastMapClient( int connectionID, FastMapEventHandler *h )
{
  conID = connectionID;

  data = NULL;
  size = 0;
  currerntPos = 0;
  handler = h;
  dataSent = 0;
}

FastMapClient::~FastMapClient()
{
  if (data)
    free(data);
}

void FastMapClient::pending ( int connectionID, void *s, unsigned int d )
{
  if ( connectionID != conID )
    return;

  char *c = (char*)malloc(d+1);
  memcpy(c,s,d);
  c[d] = 0;
  command += c;
  free (c);

  // parse the command
  std::vector<std::string> commandList = tokenize(command,std::string("\r\n"),0,false);

  if ( commandList.size() == 1 && commandList[0].size() == command.size() ) // there were no delims, we are done.
    return;

  for (unsigned int i = 0; i < (unsigned int)commandList.size();i++)
  {
    command.erase(command.begin(),command.begin()+commandList[i].size());

    std::string thisLine = commandList[i];

    if (strncmp(thisLine.c_str(),"GET",3) == 0 && !data) // it's a get and we arn't in a transfer
    {
      bz_debugMessagef(2,"FastMap: HTTP Command: %s",thisLine.c_str());
     std::vector<std::string> params = tokenize(thisLine,std::string(" "),0,false);
      if ( params.size() <= 3 )
      {
	std::string urlPath = params[1];

	if (urlPath.size()>2 && handler->mapName == urlPath.c_str()+1 )
	{
	  startTransfer(handler->mapData,handler->mapDataSize);
	}
      }
    }
    else
      bz_debugMessagef(3,"FastMap: HTTP Command: %s",thisLine.c_str());

  }
}

void FastMapClient::disconnect ( int connectionID )
{
  if (connectionID != conID)
    return;

  bz_removeNonPlayerConnectionHandler ( conID, this );
  bz_disconectNonPlayerConnection ( conID );
  conID = -1;

}
void FastMapClient::startTransfer ( unsigned char * d, unsigned int s )
{
  if (data)
    free (data);
  data = NULL;
  size = 0;
  currerntPos = 0;

  if (!s)
    return;

  bz_debugMessagef(2,"FastMap: File Transfer for connection %d started",conID);

  data = (unsigned char*)malloc(s);
  memcpy(data,d,s);
  size = s;

  std::string httpHeaders;
  httpHeaders += "HTTP/1.1 200 OK\n";
  httpHeaders += format("Content-Length: %d\n", size);
  httpHeaders += "Connection: close\n";
  httpHeaders += "Content-Type: application/octet-stream\n";
  httpHeaders += "\n";

  dataSent += (unsigned int)httpHeaders.size();
  if (!bz_sendNonPlayerData ( conID, httpHeaders.c_str(), (unsigned int)httpHeaders.size() ) || !updateTransfer())
    disconnect(conID);
}

bool FastMapClient::updateTransfer ( void )
{
  int chunkToSend = 1000;

  if ( currerntPos >= size )
    return false;

  updatTimes.push_back(bz_getCurrentTime());
  // wait till the current data is sent
  if (bz_getNonPlayerConnectionOutboundPacketCount(conID) != 0)
    return true;

  if ( currerntPos + chunkToSend > size)
    chunkToSend = size-currerntPos;

  bool worked = bz_sendNonPlayerData ( conID, data+currerntPos, chunkToSend );

  currerntPos += chunkToSend;
  dataSent += chunkToSend;

  return worked;
}

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


<commit_before>// Copyright (c) 2007, Google Inc.
// 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.

extern "C" { // needed to compile on Leopard
  #include <mach-o/nlist.h>
  #include <stdlib.h>
  #include <stdio.h>
}

#include "breakpad_nlist_64.h"
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <algorithm>
#include "client/mac/handler/dynamic_images.h"

namespace google_breakpad {

//==============================================================================
// Returns the size of the memory region containing |address| and the
// number of bytes from |address| to the end of the region.
// We potentially, will extend the size of the original
// region by the size of the following region if it's contiguous with the
// first in order to handle cases when we're reading strings and they
// straddle two vm regions.
//
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
                                          const void* address,
                                          mach_vm_size_t *size_to_end) {
  mach_vm_address_t region_base = (mach_vm_address_t)address;
  mach_vm_size_t region_size;
  natural_t nesting_level = 0;
  vm_region_submap_info_64 submap_info;
  mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;

  // Get information about the vm region containing |address|
  vm_region_recurse_info_t region_info;
  region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);

  kern_return_t result =
    mach_vm_region_recurse(target_task,
                           &region_base,
                           &region_size,
                           &nesting_level,
                           region_info,
                           &info_count);

  if (result == KERN_SUCCESS) {
    // Get distance from |address| to the end of this region
    *size_to_end = region_base + region_size -(mach_vm_address_t)address;

    // If we want to handle strings as long as 4096 characters we may need
    // to check if there's a vm region immediately following the first one.
    // If so, we need to extend |*size_to_end| to go all the way to the end
    // of the second region.
    if (*size_to_end < 4096) {
      // Second region starts where the first one ends
      mach_vm_address_t region_base2 =
        (mach_vm_address_t)(region_base + region_size);
      mach_vm_size_t region_size2;

      // Get information about the following vm region
      result =
        mach_vm_region_recurse(target_task,
                               &region_base2,
                               &region_size2,
                               &nesting_level,
                               region_info,
                               &info_count);

      // Extend region_size to go all the way to the end of the 2nd region
      if (result == KERN_SUCCESS
          && region_base2 == region_base + region_size) {
        region_size += region_size2;
      }
    }

    *size_to_end = region_base + region_size -(mach_vm_address_t)address;
  } else {
    region_size = 0;
    *size_to_end = 0;
  }

  return region_size;
}

#define kMaxStringLength 8192
//==============================================================================
// Reads a NULL-terminated string from another task.
//
// Warning!  This will not read any strings longer than kMaxStringLength-1
//
static void* ReadTaskString(task_port_t target_task,
                            const void* address) {
  // The problem is we don't know how much to read until we know how long
  // the string is. And we don't know how long the string is, until we've read
  // the memory!  So, we'll try to read kMaxStringLength bytes
  // (or as many bytes as we can until we reach the end of the vm region).
  mach_vm_size_t size_to_end;
  GetMemoryRegionSize(target_task, address, &size_to_end);

  if (size_to_end > 0) {
    mach_vm_size_t size_to_read =
      size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;

    kern_return_t kr;
    return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr);
  }

  return NULL;
}

//==============================================================================
// Reads an address range from another task.  A block of memory is malloced
// and should be freed by the caller.
void* ReadTaskMemory(task_port_t target_task,
                     const void* address,
                     size_t length,
                     kern_return_t *kr) {
  void* result = NULL;
  int systemPageSize = getpagesize();

  // use the negative of the page size for the mask to find the page address
  mach_vm_address_t page_address =
      reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize);

  mach_vm_address_t last_page_address =
      (reinterpret_cast<mach_vm_address_t>(address) + length +
       (systemPageSize - 1)) & (-systemPageSize);

  mach_vm_size_t page_size = last_page_address - page_address;
  uint8_t* local_start;
  uint32_t local_length;

  kern_return_t r;

  r = mach_vm_read(target_task,
                   page_address,
                   page_size,
                   reinterpret_cast<vm_offset_t*>(&local_start),
                   &local_length);

  if (kr != NULL) {
    *kr = r;
  }

  if (r == KERN_SUCCESS) {
    result = malloc(length);
    if (result != NULL) {
      memcpy(result,
             &local_start[(mach_vm_address_t)address - page_address],
             length);
    }
    mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
  }

  return result;
}

#pragma mark -

//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryAndVersionInfo() {
  breakpad_mach_header *header = GetMachHeader();

  // unless we can process the header, ensure that calls to
  // IsValid() will return false
  vmaddr_ = 0;
  vmsize_ = 0;
  slide_ = 0;
  version_ = 0;

  bool foundTextSection = false;
  bool foundDylibIDCommand = false;

#if __LP64__
  if(header->magic != MH_MAGIC_64) {
    return;
  }
#else
  if(header->magic != MH_MAGIC) {
    return;
  }
#endif

#ifdef __LP64__
  const uint32_t segmentLoadCommand = LC_SEGMENT_64;
#else
  const uint32_t segmentLoadCommand = LC_SEGMENT;
#endif

  const struct load_command *cmd =
    reinterpret_cast<const struct load_command *>(header + 1);

  for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
    if (!foundTextSection) {
      if (cmd->cmd == segmentLoadCommand) {
        const breakpad_mach_segment_command *seg =
            reinterpret_cast<const breakpad_mach_segment_command *>(cmd);

        if (!strcmp(seg->segname, "__TEXT")) {
          vmaddr_ = seg->vmaddr;
          vmsize_ = seg->vmsize;
          slide_ = 0;

          if (seg->fileoff == 0  &&  seg->filesize != 0) {
            slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
          }
          foundTextSection = true;
        }
      }
    }

    if (!foundDylibIDCommand) {
      if (cmd->cmd == LC_ID_DYLIB) {
        const struct dylib_command *dc =
            reinterpret_cast<const struct dylib_command *>(cmd);

        version_ = dc->dylib.current_version;
        foundDylibIDCommand = true;
      }
    }

    if (foundDylibIDCommand && foundTextSection) {
      return;
    }

    cmd = reinterpret_cast<const struct load_command *>
      (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
  }

}

void DynamicImage::Print() {
  const char *path = GetFilePath();
  if (!path) {
    path = "(unknown)";
  }
  printf("%p: %s\n", GetLoadAddress(), path);
  breakpad_mach_header *header = GetMachHeader();
  MachHeader(*header).Print();
  printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
  printf("vmsize\t\t: %llu\n", GetVMSize());
  printf("slide\t\t: %td\n", GetVMAddrSlide());
}

#pragma mark -

//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
  : task_(task), image_list_() {
  ReadImageInfoForTask();
}

void* DynamicImages::GetDyldAllImageInfosPointer() {
  const char *imageSymbolName = "_dyld_all_image_infos";
  const char *dyldPath = "/usr/lib/dyld";
#ifndef __LP64__
  struct nlist l[8];
  memset(l, 0, sizeof(l) );

  // First we lookup the address of the "_dyld_all_image_infos" struct
  // which lives in "dyld".  This structure contains information about all
  // of the loaded dynamic images.
  struct nlist &list = l[0];
  list.n_un.n_name = const_cast<char *>(imageSymbolName);
  nlist(dyldPath,&list);
  if(list.n_value) {
    return reinterpret_cast<void*>(list.n_value);
  }

  return NULL;
#else
  struct nlist_64 l[8];
  struct nlist_64 &list = l[0];

  memset(l, 0, sizeof(l) );

  const char *symbolNames[2] = { imageSymbolName, "\0" };

  int invalidEntriesCount = breakpad_nlist_64(dyldPath,&list,symbolNames);

  if(invalidEntriesCount != 0) {
    return NULL;
  }
  assert(list.n_value);
  return reinterpret_cast<void*>(list.n_value);
#endif

}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
void DynamicImages::ReadImageInfoForTask() {
  void *imageList = GetDyldAllImageInfosPointer();

  if (imageList) {
    kern_return_t kr;
    // Read the structure inside of dyld that contains information about
    // loaded images.  We're reading from the desired task's address space.

    // Here we make the assumption that dyld loaded at the same address in
    // the crashed process vs. this one.  This is an assumption made in
    // "dyld_debug.c" and is said to be nearly always valid.
    dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*>
      (ReadTaskMemory(task_,
                      reinterpret_cast<void*>(imageList),
                      sizeof(dyld_all_image_infos), &kr));

    if (dyldInfo) {
      // number of loaded images
      int count = dyldInfo->infoArrayCount;

      // Read an array of dyld_image_info structures each containing
      // information about a loaded image.
      dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
        (ReadTaskMemory(task_,
                        dyldInfo->infoArray,
                        count*sizeof(dyld_image_info), &kr));

      image_list_.reserve(count);

      for (int i = 0; i < count; ++i) {
        dyld_image_info &info = infoArray[i];

        // First read just the mach_header from the image in the task.
        breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
          (ReadTaskMemory(task_,
                          info.load_address_,
                          sizeof(breakpad_mach_header), &kr));

        if (!header)
          break;   // bail on this dynamic image

        // Now determine the total amount we really want to read based on the
        // size of the load commands.  We need the header plus all of the
        // load commands.
        size_t header_size =
            sizeof(breakpad_mach_header) + header->sizeofcmds;

        free(header);

        header = reinterpret_cast<breakpad_mach_header*>
          (ReadTaskMemory(task_, info.load_address_, header_size, &kr));

        // Read the file name from the task's memory space.
        char *file_path = NULL;
        if (info.file_path_) {
          // Although we're reading kMaxStringLength bytes, it's copied in the
          // the DynamicImage constructor below with the correct string length,
          // so it's not really wasting memory.
          file_path = reinterpret_cast<char*>
            (ReadTaskString(task_, info.file_path_));
        }

        // Create an object representing this image and add it to our list.
        DynamicImage *new_image;
        new_image = new DynamicImage(header,
                                     header_size,
                                     (breakpad_mach_header*)info.load_address_,
                                     file_path,
                                     info.file_mod_date_,
                                     task_);

        if (new_image->IsValid()) {
          image_list_.push_back(DynamicImageRef(new_image));
        } else {
          delete new_image;
        }

        if (file_path) {
          free(file_path);
        }
      }

      free(dyldInfo);
      free(infoArray);

      // sorts based on loading address
      sort(image_list_.begin(), image_list_.end() );
      // remove duplicates - this happens in certain strange cases
      // You can see it in DashboardClient when Google Gadgets plugin
      // is installed.  Apple's crash reporter log and gdb "info shared"
      // both show the same library multiple times at the same address

      vector<DynamicImageRef>::iterator it = unique(image_list_.begin(),
                                                    image_list_.end() );
      image_list_.erase(it, image_list_.end());
    }
  }
}

//==============================================================================
DynamicImage  *DynamicImages::GetExecutableImage() {
  int executable_index = GetExecutableImageIndex();

  if (executable_index >= 0) {
    return GetImage(executable_index);
  }

  return NULL;
}

//==============================================================================
// returns -1 if failure to find executable
int DynamicImages::GetExecutableImageIndex() {
  int image_count = GetImageCount();

  for (int i = 0; i < image_count; ++i) {
    DynamicImage  *image = GetImage(i);
    if (image->GetMachHeader()->filetype == MH_EXECUTE) {
      return i;
    }
  }

  return -1;
}

}  // namespace google_breakpad
<commit_msg>review: http://breakpad.appspot.com/139001<commit_after>// Copyright (c) 2007, Google Inc.
// 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.

extern "C" { // needed to compile on Leopard
  #include <mach-o/nlist.h>
  #include <stdlib.h>
  #include <stdio.h>
}

#include "breakpad_nlist_64.h"
#include <assert.h>
#include <dlfcn.h>
#include <mach/mach_vm.h>
#include <algorithm>
#include "client/mac/handler/dynamic_images.h"

namespace google_breakpad {

//==============================================================================
// Returns the size of the memory region containing |address| and the
// number of bytes from |address| to the end of the region.
// We potentially, will extend the size of the original
// region by the size of the following region if it's contiguous with the
// first in order to handle cases when we're reading strings and they
// straddle two vm regions.
//
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task,
                                          const void* address,
                                          mach_vm_size_t *size_to_end) {
  mach_vm_address_t region_base = (mach_vm_address_t)address;
  mach_vm_size_t region_size;
  natural_t nesting_level = 0;
  vm_region_submap_info_64 submap_info;
  mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64;

  // Get information about the vm region containing |address|
  vm_region_recurse_info_t region_info;
  region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info);

  kern_return_t result =
    mach_vm_region_recurse(target_task,
                           &region_base,
                           &region_size,
                           &nesting_level,
                           region_info,
                           &info_count);

  if (result == KERN_SUCCESS) {
    // Get distance from |address| to the end of this region
    *size_to_end = region_base + region_size -(mach_vm_address_t)address;

    // If we want to handle strings as long as 4096 characters we may need
    // to check if there's a vm region immediately following the first one.
    // If so, we need to extend |*size_to_end| to go all the way to the end
    // of the second region.
    if (*size_to_end < 4096) {
      // Second region starts where the first one ends
      mach_vm_address_t region_base2 =
        (mach_vm_address_t)(region_base + region_size);
      mach_vm_size_t region_size2;

      // Get information about the following vm region
      result =
        mach_vm_region_recurse(target_task,
                               &region_base2,
                               &region_size2,
                               &nesting_level,
                               region_info,
                               &info_count);

      // Extend region_size to go all the way to the end of the 2nd region
      if (result == KERN_SUCCESS
          && region_base2 == region_base + region_size) {
        region_size += region_size2;
      }
    }

    *size_to_end = region_base + region_size -(mach_vm_address_t)address;
  } else {
    region_size = 0;
    *size_to_end = 0;
  }

  return region_size;
}

#define kMaxStringLength 8192
//==============================================================================
// Reads a NULL-terminated string from another task.
//
// Warning!  This will not read any strings longer than kMaxStringLength-1
//
static void* ReadTaskString(task_port_t target_task,
                            const void* address) {
  // The problem is we don't know how much to read until we know how long
  // the string is. And we don't know how long the string is, until we've read
  // the memory!  So, we'll try to read kMaxStringLength bytes
  // (or as many bytes as we can until we reach the end of the vm region).
  mach_vm_size_t size_to_end;
  GetMemoryRegionSize(target_task, address, &size_to_end);

  if (size_to_end > 0) {
    mach_vm_size_t size_to_read =
      size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end;

    kern_return_t kr;
    return ReadTaskMemory(target_task, address, (size_t)size_to_read, &kr);
  }

  return NULL;
}

//==============================================================================
// Reads an address range from another task.  A block of memory is malloced
// and should be freed by the caller.
void* ReadTaskMemory(task_port_t target_task,
                     const void* address,
                     size_t length,
                     kern_return_t *kr) {
  void* result = NULL;
  int systemPageSize = getpagesize();

  // use the negative of the page size for the mask to find the page address
  mach_vm_address_t page_address =
      reinterpret_cast<mach_vm_address_t>(address) & (-systemPageSize);

  mach_vm_address_t last_page_address =
      (reinterpret_cast<mach_vm_address_t>(address) + length +
       (systemPageSize - 1)) & (-systemPageSize);

  mach_vm_size_t page_size = last_page_address - page_address;
  uint8_t* local_start;
  uint32_t local_length;

  kern_return_t r;

  r = mach_vm_read(target_task,
                   page_address,
                   page_size,
                   reinterpret_cast<vm_offset_t*>(&local_start),
                   &local_length);

  if (kr != NULL) {
    *kr = r;
  }

  if (r == KERN_SUCCESS) {
    result = malloc(length);
    if (result != NULL) {
      memcpy(result,
             &local_start[(mach_vm_address_t)address - page_address],
             length);
    }
    mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length);
  }

  return result;
}

#pragma mark -

//==============================================================================
// Initializes vmaddr_, vmsize_, and slide_
void DynamicImage::CalculateMemoryAndVersionInfo() {
  breakpad_mach_header *header = GetMachHeader();

  // unless we can process the header, ensure that calls to
  // IsValid() will return false
  vmaddr_ = 0;
  vmsize_ = 0;
  slide_ = 0;
  version_ = 0;

  bool foundTextSection = false;
  bool foundDylibIDCommand = false;

#if __LP64__
  if(header->magic != MH_MAGIC_64) {
    return;
  }
#else
  if(header->magic != MH_MAGIC) {
    return;
  }
#endif

#ifdef __LP64__
  const uint32_t segmentLoadCommand = LC_SEGMENT_64;
#else
  const uint32_t segmentLoadCommand = LC_SEGMENT;
#endif

  const struct load_command *cmd =
    reinterpret_cast<const struct load_command *>(header + 1);

  for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) {
    if (!foundTextSection) {
      if (cmd->cmd == segmentLoadCommand) {
        const breakpad_mach_segment_command *seg =
            reinterpret_cast<const breakpad_mach_segment_command *>(cmd);

        if (!strcmp(seg->segname, "__TEXT")) {
          vmaddr_ = seg->vmaddr;
          vmsize_ = seg->vmsize;
          slide_ = 0;

          if (seg->fileoff == 0  &&  seg->filesize != 0) {
            slide_ = (uintptr_t)GetLoadAddress() - (uintptr_t)seg->vmaddr;
          }
          foundTextSection = true;
        }
      }
    }

    if (!foundDylibIDCommand) {
      if (cmd->cmd == LC_ID_DYLIB) {
        const struct dylib_command *dc =
            reinterpret_cast<const struct dylib_command *>(cmd);

        version_ = dc->dylib.current_version;
        foundDylibIDCommand = true;
      }
    }

    if (foundDylibIDCommand && foundTextSection) {
      return;
    }

    cmd = reinterpret_cast<const struct load_command *>
      (reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
  }

}

void DynamicImage::Print() {
  const char *path = GetFilePath();
  if (!path) {
    path = "(unknown)";
  }
  printf("%p: %s\n", GetLoadAddress(), path);
  breakpad_mach_header *header = GetMachHeader();
  MachHeader(*header).Print();
  printf("vmaddr\t\t: %p\n", reinterpret_cast<void*>(GetVMAddr()));
  printf("vmsize\t\t: %llu\n", GetVMSize());
  printf("slide\t\t: %td\n", GetVMAddrSlide());
}

#pragma mark -

//==============================================================================
// Loads information about dynamically loaded code in the given task.
DynamicImages::DynamicImages(mach_port_t task)
  : task_(task), image_list_() {
  ReadImageInfoForTask();
}

void* DynamicImages::GetDyldAllImageInfosPointer() {
  const char *imageSymbolName = "_dyld_all_image_infos";
  const char *dyldPath = "/usr/lib/dyld";
#ifndef __LP64__
  struct nlist l[8];
  memset(l, 0, sizeof(l) );

  // First we lookup the address of the "_dyld_all_image_infos" struct
  // which lives in "dyld".  This structure contains information about all
  // of the loaded dynamic images.
  struct nlist &list = l[0];
  list.n_un.n_name = const_cast<char *>(imageSymbolName);
  nlist(dyldPath,&list);
  if(list.n_value) {
    return reinterpret_cast<void*>(list.n_value);
  }

  return NULL;
#else
  struct nlist_64 l[8];
  struct nlist_64 &list = l[0];

  memset(l, 0, sizeof(l) );

  const char *symbolNames[2] = { imageSymbolName, "\0" };

  int invalidEntriesCount = breakpad_nlist_64(dyldPath,&list,symbolNames);

  if(invalidEntriesCount != 0) {
    return NULL;
  }
  assert(list.n_value);
  return reinterpret_cast<void*>(list.n_value);
#endif

}
//==============================================================================
// This code was written using dyld_debug.c (from Darwin) as a guide.
void DynamicImages::ReadImageInfoForTask() {
  void *imageList = GetDyldAllImageInfosPointer();

  if (imageList) {
    kern_return_t kr;
    // Read the structure inside of dyld that contains information about
    // loaded images.  We're reading from the desired task's address space.

    // Here we make the assumption that dyld loaded at the same address in
    // the crashed process vs. this one.  This is an assumption made in
    // "dyld_debug.c" and is said to be nearly always valid.
    dyld_all_image_infos *dyldInfo = reinterpret_cast<dyld_all_image_infos*>
      (ReadTaskMemory(task_,
                      reinterpret_cast<void*>(imageList),
                      sizeof(dyld_all_image_infos), &kr));

    if (dyldInfo) {
      // number of loaded images
      int count = dyldInfo->infoArrayCount;

      // Read an array of dyld_image_info structures each containing
      // information about a loaded image.
      dyld_image_info *infoArray = reinterpret_cast<dyld_image_info*>
        (ReadTaskMemory(task_,
                        dyldInfo->infoArray,
                        count*sizeof(dyld_image_info), &kr));

      image_list_.reserve(count);

      for (int i = 0; i < count; ++i) {
        dyld_image_info &info = infoArray[i];

        // First read just the mach_header from the image in the task.
        breakpad_mach_header *header = reinterpret_cast<breakpad_mach_header*>
          (ReadTaskMemory(task_,
                          info.load_address_,
                          sizeof(breakpad_mach_header), &kr));

        if (!header)
          break;   // bail on this dynamic image

        // Now determine the total amount we really want to read based on the
        // size of the load commands.  We need the header plus all of the
        // load commands.
        size_t header_size =
            sizeof(breakpad_mach_header) + header->sizeofcmds;

        free(header);

        header = reinterpret_cast<breakpad_mach_header*>
          (ReadTaskMemory(task_, info.load_address_, header_size, &kr));

        // Read the file name from the task's memory space.
        char *file_path = NULL;
        if (info.file_path_) {
          // Although we're reading kMaxStringLength bytes, it's copied in the
          // the DynamicImage constructor below with the correct string length,
          // so it's not really wasting memory.
          file_path = reinterpret_cast<char*>
            (ReadTaskString(task_, info.file_path_));
        }

        // Create an object representing this image and add it to our list.
        DynamicImage *new_image;
        new_image = new DynamicImage(header,
                                     header_size,
                                     (breakpad_mach_header*)info.load_address_,
                                     file_path,
                                     info.file_mod_date_,
                                     task_);

        if (new_image->IsValid()) {
          image_list_.push_back(DynamicImageRef(new_image));
        } else {
          delete new_image;
        }

        if (file_path) {
          free(file_path);
        }
      }

      free(dyldInfo);
      free(infoArray);

      // sorts based on loading address
      sort(image_list_.begin(), image_list_.end() );
      // remove duplicates - this happens in certain strange cases
      // You can see it in DashboardClient when Google Gadgets plugin
      // is installed.  Apple's crash reporter log and gdb "info shared"
      // both show the same library multiple times at the same address

      vector<DynamicImageRef>::iterator it = unique(image_list_.begin(),
                                                    image_list_.end() );
      image_list_.erase(it, image_list_.end());
    }
  }
}

//==============================================================================
DynamicImage  *DynamicImages::GetExecutableImage() {
  int executable_index = GetExecutableImageIndex();

  if (executable_index >= 0) {
    return GetImage(executable_index);
  }

  return NULL;
}

//==============================================================================
// returns -1 if failure to find executable
int DynamicImages::GetExecutableImageIndex() {
  int image_count = GetImageCount();

  for (int i = 0; i < image_count; ++i) {
    DynamicImage  *image = GetImage(i);
    if (image->GetMachHeader()->filetype == MH_EXECUTE) {
      return i;
    }
  }

  return -1;
}

}  // namespace google_breakpad
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1683
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1097001 by johtaylo@johtaylo-JTBUILDER03-increment on 2014/11/14 03:00:12<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1684
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1959
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1215666 by johtaylo@johtaylo-JTBUILDER03-increment on 2015/11/30 03:00:10<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1960
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2697
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1582604 by chui@ocl-promo-incrementor on 2018/07/19 02:56:32<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2698
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1628
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1074167 by johtaylo@johtaylo-JTBUILDER03-increment on 2014/09/06 03:00:11<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
# define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
# define AMD_PLATFORM_BUILD_NUMBER 1629
#endif // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
# define AMD_PLATFORM_REVISION_NUMBER 0
#endif // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
# define AMD_PLATFORM_RELEASE_INFO
#endif // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING XSTR(AMD_PLATFORM_BUILD_NUMBER) \
    "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
# define AMD_PLATFORM_INFO "AMD-APP" AMD_PLATFORM_RELEASE_INFO \
    DEBUG_ONLY("." IF(IS_OPTIMIZED,"opt","dbg")) " (" AMD_BUILD_STRING ")"
#endif // ATI_PLATFORM_INFO

#endif // VERSIONS_HPP_
<|endoftext|>


<commit_before>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2841
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>P4 to Git Change 1748484 by chui@ocl-promo-incrementor on 2019/02/26 03:00:23<commit_after>//
// Copyright (c) 2010 Advanced Micro Devices, Inc. All rights reserved.
//

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 2842
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"

#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) &&                         \
    !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA)

#include "vm/malloc_hooks.h"

#include "gperftools/malloc_hook.h"

#include "platform/assert.h"
#include "vm/hash_map.h"
#include "vm/json_stream.h"
#include "vm/os_thread.h"
#include "vm/profiler.h"

namespace dart {

class AddressMap;

// MallocHooksState contains all of the state related to the configuration of
// the malloc hooks, allocation information, and locks.
class MallocHooksState : public AllStatic {
 public:
  static void RecordAllocHook(const void* ptr, size_t size);
  static void RecordFreeHook(const void* ptr);

  static bool Active() {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    return active_;
  }
  static void Init();

  static bool ProfilingEnabled() { return (OSThread::TryCurrent() != NULL); }

  static bool stack_trace_collection_enabled() {
    return stack_trace_collection_enabled_;
  }

  static void set_stack_trace_collection_enabled(bool enabled) {
    stack_trace_collection_enabled_ = enabled;
  }

  static bool IsOriginalProcess() {
    ASSERT(original_pid_ != kInvalidPid);
    return original_pid_ == OS::ProcessId();
  }

  static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
  static ThreadId* malloc_hook_mutex_owner() {
    return &malloc_hook_mutex_owner_;
  }
  static bool IsLockHeldByCurrentThread() {
    return (malloc_hook_mutex_owner_ == OSThread::GetCurrentThreadId());
  }

  static intptr_t allocation_count() { return allocation_count_; }

  static intptr_t heap_allocated_memory_in_bytes() {
    return heap_allocated_memory_in_bytes_;
  }

  static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    ASSERT(size >= 0);
    heap_allocated_memory_in_bytes_ += size;
    ++allocation_count_;
  }

  static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    ASSERT(size >= 0);
    ASSERT(heap_allocated_memory_in_bytes_ >= size);
    heap_allocated_memory_in_bytes_ -= size;
    --allocation_count_;
    ASSERT(allocation_count_ >= 0);
  }

  static AddressMap* address_map() { return address_map_; }

  static void ResetStats();
  static void TearDown();

 private:
  static Mutex* malloc_hook_mutex_;
  static ThreadId malloc_hook_mutex_owner_;

  // Variables protected by malloc_hook_mutex_.
  static bool active_;
  static bool stack_trace_collection_enabled_;
  static intptr_t allocation_count_;
  static intptr_t heap_allocated_memory_in_bytes_;
  static AddressMap* address_map_;
  // End protected variables.

  static intptr_t original_pid_;
  static const intptr_t kInvalidPid = -1;
};

// A locker-type class similar to MutexLocker which tracks which thread
// currently holds the lock. We use this instead of MutexLocker and
// mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only
// enabled for debug mode.
class MallocLocker : public ValueObject {
 public:
  explicit MallocLocker(Mutex* mutex, ThreadId* owner)
      : mutex_(mutex), owner_(owner) {
    ASSERT(owner != NULL);
    mutex_->Lock();
    ASSERT(*owner_ == OSThread::kInvalidThreadId);
    *owner_ = OSThread::GetCurrentThreadId();
  }

  virtual ~MallocLocker() {
    ASSERT(*owner_ == OSThread::GetCurrentThreadId());
    *owner_ = OSThread::kInvalidThreadId;
    mutex_->Unlock();
  }

 private:
  Mutex* mutex_;
  ThreadId* owner_;
};

// AllocationInfo contains all information related to a given allocation
// including:
//   -Allocation size in bytes
//   -Stack trace corresponding to the location of allocation, if applicable
class AllocationInfo {
 public:
  explicit AllocationInfo(intptr_t allocation_size)
      : sample_(NULL), allocation_size_(allocation_size) {
    // Stack trace collection is disabled when we are in the process of creating
    // the first OSThread in order to prevent deadlocks.
    if (MallocHooksState::ProfilingEnabled() &&
        MallocHooksState::stack_trace_collection_enabled()) {
      sample_ = Profiler::SampleNativeAllocation(kSkipCount);
    }
  }

  Sample* sample() const { return sample_; }
  intptr_t allocation_size() const { return allocation_size_; }

 private:
  Sample* sample_;
  intptr_t allocation_size_;

  // The number of frames that are generated by the malloc hooks and collection
  // of the stack trace. These frames are ignored when collecting the stack
  // trace for a memory allocation. If this number is incorrect, some tests in
  // malloc_hook_tests.cc might fail, particularily
  // StackTraceMallocHookLengthTest. If this value is updated, please make sure
  // that the MallocHooks test cases pass on all platforms.
  static const intptr_t kSkipCount = 5;
};

// Custom key/value trait specifically for address/size pairs. Unlike
// RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
class AddressKeyValueTrait : public AllStatic {
 public:
  typedef const void* Key;
  typedef AllocationInfo* Value;

  struct Pair {
    Key key;
    Value value;
    Pair() : key(NULL), value(NULL) {}
    Pair(const Key key, const Value& value) : key(key), value(value) {}
    Pair(const Pair& other) : key(other.key), value(other.value) {}
  };

  static Key KeyOf(Pair kv) { return kv.key; }
  static Value ValueOf(Pair kv) { return kv.value; }
  static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
  static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};

// Map class that will be used to store mappings between ptr -> allocation size.
class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
 public:
  typedef AddressKeyValueTrait::Key Key;
  typedef AddressKeyValueTrait::Value Value;
  typedef AddressKeyValueTrait::Pair Pair;

  virtual ~AddressMap() { Clear(); }

  void Insert(const Key& key, const Value& value) {
    Pair pair(key, value);
    MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
  }

  bool Lookup(const Key& key, Value* value) {
    ASSERT(value != NULL);
    Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
    if (pair == NULL) {
      return false;
    } else {
      *value = pair->value;
      return true;
    }
  }

  void Clear() {
    Iterator iter = GetIterator();
    Pair* result = iter.Next();
    while (result != NULL) {
      delete result->value;
      result->value = NULL;
      result = iter.Next();
    }
    MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
  }
};

// MallocHooks state / locks.
bool MallocHooksState::active_ = false;
bool MallocHooksState::stack_trace_collection_enabled_ = false;
intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
ThreadId MallocHooksState::malloc_hook_mutex_owner_ =
    OSThread::kInvalidThreadId;

// Memory allocation state information.
intptr_t MallocHooksState::allocation_count_ = 0;
intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
AddressMap* MallocHooksState::address_map_ = NULL;

void MallocHooksState::Init() {
  address_map_ = new AddressMap();
  active_ = true;
#if defined(DEBUG)
  stack_trace_collection_enabled_ = true;
#else
  stack_trace_collection_enabled_ = false;
#endif  // defined(DEBUG)
  original_pid_ = OS::ProcessId();
}

void MallocHooksState::ResetStats() {
  ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
  allocation_count_ = 0;
  heap_allocated_memory_in_bytes_ = 0;
  address_map_->Clear();
}

void MallocHooksState::TearDown() {
  ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
  active_ = false;
  original_pid_ = kInvalidPid;
  ResetStats();
  delete address_map_;
  address_map_ = NULL;
}

void MallocHooks::InitOnce() {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  ASSERT(!MallocHooksState::Active());

  MallocHooksState::Init();

  // Register malloc hooks.
  bool success = false;
  success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook);
  ASSERT(success);
  success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook);
  ASSERT(success);
}

void MallocHooks::TearDown() {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  ASSERT(MallocHooksState::Active());

  // Remove malloc hooks.
  bool success = false;
  success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
  ASSERT(success);
  success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
  ASSERT(success);

  MallocHooksState::TearDown();
}

bool MallocHooks::ProfilingEnabled() {
  return MallocHooksState::ProfilingEnabled();
}

bool MallocHooks::stack_trace_collection_enabled() {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::stack_trace_collection_enabled();
}

void MallocHooks::set_stack_trace_collection_enabled(bool enabled) {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  MallocHooksState::set_stack_trace_collection_enabled(enabled);
}

void MallocHooks::ResetStats() {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  if (MallocHooksState::Active()) {
    MallocHooksState::ResetStats();
  }
}

bool MallocHooks::Active() {
  if (!FLAG_enable_malloc_hooks) {
    return false;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());

  return MallocHooksState::Active();
}

void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  intptr_t allocated_memory = 0;
  intptr_t allocation_count = 0;
  bool add_usage = false;
  // AddProperty may call malloc which would result in an attempt
  // to acquire the lock recursively so we extract the values first
  // and then add the JSON properties.
  {
    MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                    MallocHooksState::malloc_hook_mutex_owner());
    if (MallocHooksState::Active()) {
      allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes();
      allocation_count = MallocHooksState::allocation_count();
      add_usage = true;
    }
  }
  if (add_usage) {
    jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory);
    jsobj->AddProperty("_heapAllocationCount", allocation_count);
  }
}

intptr_t MallocHooks::allocation_count() {
  if (!FLAG_enable_malloc_hooks) {
    return 0;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::allocation_count();
}

intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
  if (!FLAG_enable_malloc_hooks) {
    return 0;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::heap_allocated_memory_in_bytes();
}

Sample* MallocHooks::GetSample(const void* ptr) {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());

  ASSERT(MallocHooksState::Active());

  if (ptr != NULL) {
    AllocationInfo* allocation_info = NULL;
    if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
      ASSERT(allocation_info != NULL);
      return allocation_info->sample();
    }
  }
  return NULL;
}

void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
  if (MallocHooksState::IsLockHeldByCurrentThread() ||
      !MallocHooksState::IsOriginalProcess()) {
    return;
  }

  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  // Now that we hold the lock, check to make sure everything is still active.
  if ((ptr != NULL) && MallocHooksState::Active()) {
    MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
    MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
  }
}

void MallocHooksState::RecordFreeHook(const void* ptr) {
  if (MallocHooksState::IsLockHeldByCurrentThread() ||
      !MallocHooksState::IsOriginalProcess()) {
    return;
  }

  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  // Now that we hold the lock, check to make sure everything is still active.
  if ((ptr != NULL) && MallocHooksState::Active()) {
    AllocationInfo* allocation_info = NULL;
    if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
      MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
          allocation_info->allocation_size());
      MallocHooksState::address_map()->Remove(ptr);
      delete allocation_info;
    }
  }
}

}  // namespace dart

#endif  // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) &&
        // !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA)
<commit_msg>Fixed issue where MallocHooks tests would try and re-initialize the hooks and ended up hitting an assert in debug mode.<commit_after>// Copyright (c) 2017, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#include "platform/globals.h"

#if defined(DART_USE_TCMALLOC) && !defined(PRODUCT) &&                         \
    !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA)

#include "vm/malloc_hooks.h"

#include "gperftools/malloc_hook.h"

#include "platform/assert.h"
#include "vm/hash_map.h"
#include "vm/json_stream.h"
#include "vm/os_thread.h"
#include "vm/profiler.h"

namespace dart {

class AddressMap;

// MallocHooksState contains all of the state related to the configuration of
// the malloc hooks, allocation information, and locks.
class MallocHooksState : public AllStatic {
 public:
  static void RecordAllocHook(const void* ptr, size_t size);
  static void RecordFreeHook(const void* ptr);

  static bool Active() {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    return active_;
  }
  static void Init();

  static bool ProfilingEnabled() { return (OSThread::TryCurrent() != NULL); }

  static bool stack_trace_collection_enabled() {
    return stack_trace_collection_enabled_;
  }

  static void set_stack_trace_collection_enabled(bool enabled) {
    stack_trace_collection_enabled_ = enabled;
  }

  static bool IsOriginalProcess() {
    ASSERT(original_pid_ != kInvalidPid);
    return original_pid_ == OS::ProcessId();
  }

  static Mutex* malloc_hook_mutex() { return malloc_hook_mutex_; }
  static ThreadId* malloc_hook_mutex_owner() {
    return &malloc_hook_mutex_owner_;
  }
  static bool IsLockHeldByCurrentThread() {
    return (malloc_hook_mutex_owner_ == OSThread::GetCurrentThreadId());
  }

  static intptr_t allocation_count() { return allocation_count_; }

  static intptr_t heap_allocated_memory_in_bytes() {
    return heap_allocated_memory_in_bytes_;
  }

  static void IncrementHeapAllocatedMemoryInBytes(intptr_t size) {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    ASSERT(size >= 0);
    heap_allocated_memory_in_bytes_ += size;
    ++allocation_count_;
  }

  static void DecrementHeapAllocatedMemoryInBytes(intptr_t size) {
    ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
    ASSERT(size >= 0);
    ASSERT(heap_allocated_memory_in_bytes_ >= size);
    heap_allocated_memory_in_bytes_ -= size;
    --allocation_count_;
    ASSERT(allocation_count_ >= 0);
  }

  static AddressMap* address_map() { return address_map_; }

  static void ResetStats();
  static void TearDown();

 private:
  static Mutex* malloc_hook_mutex_;
  static ThreadId malloc_hook_mutex_owner_;

  // Variables protected by malloc_hook_mutex_.
  static bool active_;
  static bool stack_trace_collection_enabled_;
  static intptr_t allocation_count_;
  static intptr_t heap_allocated_memory_in_bytes_;
  static AddressMap* address_map_;
  // End protected variables.

  static intptr_t original_pid_;
  static const intptr_t kInvalidPid = -1;
};

// A locker-type class similar to MutexLocker which tracks which thread
// currently holds the lock. We use this instead of MutexLocker and
// mutex->IsOwnedByCurrentThread() since IsOwnedByCurrentThread() is only
// enabled for debug mode.
class MallocLocker : public ValueObject {
 public:
  explicit MallocLocker(Mutex* mutex, ThreadId* owner)
      : mutex_(mutex), owner_(owner) {
    ASSERT(owner != NULL);
    mutex_->Lock();
    ASSERT(*owner_ == OSThread::kInvalidThreadId);
    *owner_ = OSThread::GetCurrentThreadId();
  }

  virtual ~MallocLocker() {
    ASSERT(*owner_ == OSThread::GetCurrentThreadId());
    *owner_ = OSThread::kInvalidThreadId;
    mutex_->Unlock();
  }

 private:
  Mutex* mutex_;
  ThreadId* owner_;
};

// AllocationInfo contains all information related to a given allocation
// including:
//   -Allocation size in bytes
//   -Stack trace corresponding to the location of allocation, if applicable
class AllocationInfo {
 public:
  explicit AllocationInfo(intptr_t allocation_size)
      : sample_(NULL), allocation_size_(allocation_size) {
    // Stack trace collection is disabled when we are in the process of creating
    // the first OSThread in order to prevent deadlocks.
    if (MallocHooksState::ProfilingEnabled() &&
        MallocHooksState::stack_trace_collection_enabled()) {
      sample_ = Profiler::SampleNativeAllocation(kSkipCount);
    }
  }

  Sample* sample() const { return sample_; }
  intptr_t allocation_size() const { return allocation_size_; }

 private:
  Sample* sample_;
  intptr_t allocation_size_;

  // The number of frames that are generated by the malloc hooks and collection
  // of the stack trace. These frames are ignored when collecting the stack
  // trace for a memory allocation. If this number is incorrect, some tests in
  // malloc_hook_tests.cc might fail, particularily
  // StackTraceMallocHookLengthTest. If this value is updated, please make sure
  // that the MallocHooks test cases pass on all platforms.
  static const intptr_t kSkipCount = 5;
};

// Custom key/value trait specifically for address/size pairs. Unlike
// RawPointerKeyValueTrait, the default value is -1 as 0 can be a valid entry.
class AddressKeyValueTrait : public AllStatic {
 public:
  typedef const void* Key;
  typedef AllocationInfo* Value;

  struct Pair {
    Key key;
    Value value;
    Pair() : key(NULL), value(NULL) {}
    Pair(const Key key, const Value& value) : key(key), value(value) {}
    Pair(const Pair& other) : key(other.key), value(other.value) {}
  };

  static Key KeyOf(Pair kv) { return kv.key; }
  static Value ValueOf(Pair kv) { return kv.value; }
  static intptr_t Hashcode(Key key) { return reinterpret_cast<intptr_t>(key); }
  static bool IsKeyEqual(Pair kv, Key key) { return kv.key == key; }
};

// Map class that will be used to store mappings between ptr -> allocation size.
class AddressMap : public MallocDirectChainedHashMap<AddressKeyValueTrait> {
 public:
  typedef AddressKeyValueTrait::Key Key;
  typedef AddressKeyValueTrait::Value Value;
  typedef AddressKeyValueTrait::Pair Pair;

  virtual ~AddressMap() { Clear(); }

  void Insert(const Key& key, const Value& value) {
    Pair pair(key, value);
    MallocDirectChainedHashMap<AddressKeyValueTrait>::Insert(pair);
  }

  bool Lookup(const Key& key, Value* value) {
    ASSERT(value != NULL);
    Pair* pair = MallocDirectChainedHashMap<AddressKeyValueTrait>::Lookup(key);
    if (pair == NULL) {
      return false;
    } else {
      *value = pair->value;
      return true;
    }
  }

  void Clear() {
    Iterator iter = GetIterator();
    Pair* result = iter.Next();
    while (result != NULL) {
      delete result->value;
      result->value = NULL;
      result = iter.Next();
    }
    MallocDirectChainedHashMap<AddressKeyValueTrait>::Clear();
  }
};

// MallocHooks state / locks.
bool MallocHooksState::active_ = false;
bool MallocHooksState::stack_trace_collection_enabled_ = false;
intptr_t MallocHooksState::original_pid_ = MallocHooksState::kInvalidPid;
Mutex* MallocHooksState::malloc_hook_mutex_ = new Mutex();
ThreadId MallocHooksState::malloc_hook_mutex_owner_ =
    OSThread::kInvalidThreadId;

// Memory allocation state information.
intptr_t MallocHooksState::allocation_count_ = 0;
intptr_t MallocHooksState::heap_allocated_memory_in_bytes_ = 0;
AddressMap* MallocHooksState::address_map_ = NULL;

void MallocHooksState::Init() {
  address_map_ = new AddressMap();
  active_ = true;
#if defined(DEBUG)
  stack_trace_collection_enabled_ = true;
#else
  stack_trace_collection_enabled_ = false;
#endif  // defined(DEBUG)
  original_pid_ = OS::ProcessId();
}

void MallocHooksState::ResetStats() {
  ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
  allocation_count_ = 0;
  heap_allocated_memory_in_bytes_ = 0;
  address_map_->Clear();
}

void MallocHooksState::TearDown() {
  ASSERT(malloc_hook_mutex()->IsOwnedByCurrentThread());
  active_ = false;
  original_pid_ = kInvalidPid;
  ResetStats();
  delete address_map_;
  address_map_ = NULL;
}

void MallocHooks::InitOnce() {
  if (!FLAG_enable_malloc_hooks || MallocHooks::Active()) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  ASSERT(!MallocHooksState::Active());

  MallocHooksState::Init();

  // Register malloc hooks.
  bool success = false;
  success = MallocHook::AddNewHook(&MallocHooksState::RecordAllocHook);
  ASSERT(success);
  success = MallocHook::AddDeleteHook(&MallocHooksState::RecordFreeHook);
  ASSERT(success);
}

void MallocHooks::TearDown() {
  if (!FLAG_enable_malloc_hooks || !MallocHooks::Active()) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  ASSERT(MallocHooksState::Active());

  // Remove malloc hooks.
  bool success = false;
  success = MallocHook::RemoveNewHook(&MallocHooksState::RecordAllocHook);
  ASSERT(success);
  success = MallocHook::RemoveDeleteHook(&MallocHooksState::RecordFreeHook);
  ASSERT(success);

  MallocHooksState::TearDown();
}

bool MallocHooks::ProfilingEnabled() {
  return MallocHooksState::ProfilingEnabled();
}

bool MallocHooks::stack_trace_collection_enabled() {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::stack_trace_collection_enabled();
}

void MallocHooks::set_stack_trace_collection_enabled(bool enabled) {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  MallocHooksState::set_stack_trace_collection_enabled(enabled);
}

void MallocHooks::ResetStats() {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  if (MallocHooksState::Active()) {
    MallocHooksState::ResetStats();
  }
}

bool MallocHooks::Active() {
  if (!FLAG_enable_malloc_hooks) {
    return false;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());

  return MallocHooksState::Active();
}

void MallocHooks::PrintToJSONObject(JSONObject* jsobj) {
  if (!FLAG_enable_malloc_hooks) {
    return;
  }
  intptr_t allocated_memory = 0;
  intptr_t allocation_count = 0;
  bool add_usage = false;
  // AddProperty may call malloc which would result in an attempt
  // to acquire the lock recursively so we extract the values first
  // and then add the JSON properties.
  {
    MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                    MallocHooksState::malloc_hook_mutex_owner());
    if (MallocHooksState::Active()) {
      allocated_memory = MallocHooksState::heap_allocated_memory_in_bytes();
      allocation_count = MallocHooksState::allocation_count();
      add_usage = true;
    }
  }
  if (add_usage) {
    jsobj->AddProperty("_heapAllocatedMemoryUsage", allocated_memory);
    jsobj->AddProperty("_heapAllocationCount", allocation_count);
  }
}

intptr_t MallocHooks::allocation_count() {
  if (!FLAG_enable_malloc_hooks) {
    return 0;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::allocation_count();
}

intptr_t MallocHooks::heap_allocated_memory_in_bytes() {
  if (!FLAG_enable_malloc_hooks) {
    return 0;
  }
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  return MallocHooksState::heap_allocated_memory_in_bytes();
}

Sample* MallocHooks::GetSample(const void* ptr) {
  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());

  ASSERT(MallocHooksState::Active());

  if (ptr != NULL) {
    AllocationInfo* allocation_info = NULL;
    if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
      ASSERT(allocation_info != NULL);
      return allocation_info->sample();
    }
  }
  return NULL;
}

void MallocHooksState::RecordAllocHook(const void* ptr, size_t size) {
  if (MallocHooksState::IsLockHeldByCurrentThread() ||
      !MallocHooksState::IsOriginalProcess()) {
    return;
  }

  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  // Now that we hold the lock, check to make sure everything is still active.
  if ((ptr != NULL) && MallocHooksState::Active()) {
    MallocHooksState::IncrementHeapAllocatedMemoryInBytes(size);
    MallocHooksState::address_map()->Insert(ptr, new AllocationInfo(size));
  }
}

void MallocHooksState::RecordFreeHook(const void* ptr) {
  if (MallocHooksState::IsLockHeldByCurrentThread() ||
      !MallocHooksState::IsOriginalProcess()) {
    return;
  }

  MallocLocker ml(MallocHooksState::malloc_hook_mutex(),
                  MallocHooksState::malloc_hook_mutex_owner());
  // Now that we hold the lock, check to make sure everything is still active.
  if ((ptr != NULL) && MallocHooksState::Active()) {
    AllocationInfo* allocation_info = NULL;
    if (MallocHooksState::address_map()->Lookup(ptr, &allocation_info)) {
      MallocHooksState::DecrementHeapAllocatedMemoryInBytes(
          allocation_info->allocation_size());
      MallocHooksState::address_map()->Remove(ptr);
      delete allocation_info;
    }
  }
}

}  // namespace dart

#endif  // defined(DART_USE_TCMALLOC) && !defined(PRODUCT) &&
        // !defined(TARGET_ARCH_DBC) && !defined(TARGET_OS_FUCHSIA)
<|endoftext|>