| Index: base/android/third_party/cygprofile_startup/cygprofile_startup.cc | 
| diff --git a/base/android/third_party/cygprofile_startup/cygprofile_startup.cc b/base/android/third_party/cygprofile_startup/cygprofile_startup.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..b888c95267ccdc4f72f7ff61269fc5fb1b9f1845 | 
| --- /dev/null | 
| +++ b/base/android/third_party/cygprofile_startup/cygprofile_startup.cc | 
| @@ -0,0 +1,344 @@ | 
| +/* | 
| + * cygprofile_startup.cc - CygProfiler runtime functions. | 
| + * | 
| + * Michal Ludvig <michal@logix.cz> | 
| + * http://www.logix.cz/michal/devel | 
| + * | 
| + * cygprofile_startup.cc | 
| + * - Compile your program with -finstrument-functions and link | 
| + *   together with this code. | 
| + * - Logging is enabled as soon as your program calls | 
| + *   cygprofile_enable() and disabled with cygprofile_disable(). | 
| + * - Before logging was enabled you can change the name | 
| + *   of a logfile by calling cygprofile_setfilename(). | 
| + * | 
| + * This implementation has been modified in the following ways: | 
| + *   1) Upon profile enabling, the virtual address is logged as the | 
| + *      first line to assist in symbolizing logged virutal addresses. | 
| + *   2) Only function entry for the first time a particular function | 
| + *      is entered is logged.  This is for profiling for code layout. | 
| + *   3) The implementation is made thread-safe. | 
| + *   4) The implementation logs the timestamp (seconds and microsecs). | 
| + *   5) The implementation adds function cygprofile_start(filename) which | 
| + *      calls cyg_setfilename and enables profiling and cygprofile_end() | 
| + *      to guarantee log buffer is flushed and logfile is closed. | 
| + */ | 
| + | 
| +#include <errno.h> | 
| +#include <pthread.h> | 
| +#include <stdint.h> | 
| +#include <string.h> | 
| +#include <stdlib.h> | 
| +#include <sys/time.h> | 
| +#include <time.h> | 
| +#include <unistd.h> | 
| + | 
| +#include <fstream> | 
| +#include <set> | 
| + | 
| +#include "cygprofile_startup.h" | 
| + | 
| +namespace cygprofile_startup { | 
| + | 
| +const int kMaxFileNameSizeSize = 100; | 
| + | 
| +// On Android, put the default location for the profile files on the | 
| +// SD Card partition for easy retrieval and ensuring a fixed absolute | 
| +// paths for all output. | 
| +#ifdef __ANDROID__ | 
| +static const char kDefaultFileName[] = "/sdcard/cyglog.%d"; | 
| +#else | 
| +const char* kDefaultFileName = "cyglog.%d"; | 
| +#endif | 
| +const int kMaxLineSize = 512; | 
| + | 
| +static FILE* gLogFile = NULL; | 
| +static volatile bool gCygProfileEnabled = false; | 
| +static char gCygProfileFileName[kMaxFileNameSizeSize + 1]; | 
| + | 
| +// Keeps track of all functions that have been logged. | 
| +static std::set<void*>* gFunctionsCalled = NULL; | 
| + | 
| + | 
| +// Return the current timestamp in seconds and micro-seconds | 
| +// On Linux-based systems, use the raw monotonic clock to ensure that | 
| +// the timestamps are not affected by NTP adjustments. Otherwise simply | 
| +// use gettimeofday() | 
| +#ifdef __linux__ | 
| + | 
| +// We use CLOCK_MONOTONIC_RAW on Linux, because CLOCK_MONOTONIC is broken | 
| +// and is subject to NTP adjustments since Linux 2.6.28. This bug has been | 
| +// fixed in 2.6.32 which not all devices run yet. | 
| +// | 
| +// See http://stackoverflow.com/questions/3657289/linux-clock-gettimeclock-monotonic-strange-non-monotonic-behavior | 
| +// | 
| + | 
| +// The Android Bionic headers don't necessarily define the constant. | 
| +#ifndef CLOCK_MONOTONIC_RAW | 
| +#define CLOCK_MONOTONIC_RAW  4 | 
| +#endif | 
| + | 
| +static void | 
| +get_timestamp(time_t *seconds, long *usecs) | 
| +{ | 
| +    struct timespec ts; | 
| +    clock_gettime(CLOCK_MONOTONIC_RAW, &ts); | 
| +    *seconds = ts.tv_sec; | 
| +    *usecs = ts.tv_nsec / 1000; | 
| +} | 
| + | 
| +#else // !__linux__ | 
| + | 
| +static void | 
| +get_timestamp(time_t *seconds, long *usecs) | 
| +{ | 
| +    struct timeval tv; | 
| +    gettimeofday(&tv, NULL); | 
| +    *seconds = tv.tv_sec; | 
| +    *usecs = tv.tv_usec; | 
| +} | 
| + | 
| +#endif // !__linux__ | 
| + | 
| +#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER | 
| +#  ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP | 
| +#    define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP | 
| +#  else | 
| +#    error MISSING PTHREAD_RECURSIVE_MUTEX_INITIALIZER DEFINITION! | 
| +#  endif | 
| +#endif | 
| + | 
| +// Ensure thread safety (see __cyg_profile_func_enter) | 
| +static pthread_mutex_t gMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; | 
| +static unsigned int gDepth = 0; | 
| + | 
| +#ifdef __cplusplus | 
| +extern "C" { | 
| +#endif | 
| + | 
| +  FILE* cygprofile_openlogfile(const char* filename) | 
| +      __attribute__((no_instrument_function)); | 
| +  void cygprofile_closelogfile(void) | 
| +      __attribute__((no_instrument_function)); | 
| + | 
| +  // Note that these are linked internally by the compiler. | 
| +  // Don't call them directly! | 
| +  void __cyg_profile_func_enter(void* this_fn, void* call_site) | 
| +      __attribute__((no_instrument_function)); | 
| +  void __cyg_profile_func_exit(void* this_fn, void* call_site) | 
| +      __attribute__((no_instrument_function)); | 
| + | 
| +#ifdef __cplusplus | 
| +}; | 
| +#endif | 
| + | 
| +// Called internally by instrumentation inserted by the compiler upon entering | 
| +// a function.  Instrumentation is inserted by the compiler for all functions | 
| +// whose source is compiled with the -finstrument-functions CFLAG.  The code | 
| +// logs the call along with the timestamp, process id and thread id. | 
| +void __cyg_profile_func_enter(void* this_fn, void* call_site) { | 
| +  pthread_mutex_lock(&gMutex); | 
| +  // avoid recursive calls | 
| +  if (gDepth == 0) { | 
| +    gDepth += 1; | 
| +    if (!gCygProfileEnabled) { | 
| +      cygprofile_enable(); | 
| +    } | 
| +    if (gCygProfileEnabled && gLogFile) { | 
| +      // gFunctionsCalled is initialized in cygprofile_enable() | 
| +      if (gFunctionsCalled && gFunctionsCalled->find(this_fn) == | 
| +          gFunctionsCalled->end()) { | 
| +        time_t seconds; | 
| +        long usecs; | 
| +        get_timestamp(&seconds, &usecs); | 
| +        fprintf(gLogFile, "%ld %ld\t%d:%ld\t%p\n", | 
| +                seconds, usecs, getpid(), pthread_self(), this_fn); | 
| +        fflush(gLogFile); | 
| +        gFunctionsCalled->insert(this_fn); | 
| +      } | 
| +    } | 
| +    gDepth -= 1; | 
| +  } | 
| +  pthread_mutex_unlock(&gMutex); | 
| +} | 
| + | 
| +// Called internally by instrumentation inserted by the compiler upon exiting | 
| +// a function.  Instrumentation is inserted by the compiler for all functions | 
| +// whose source is compiled with the -finstrument-functions CFLAG.  The exit | 
| +// of a function is ignored because only enter is useful for order profiling. | 
| +void __cyg_profile_func_exit(void* this_fn, void* call_site) { | 
| +  // Do not do anything on function exit. | 
| +} | 
| + | 
| +// Enables profiling and writes the first lines to the log file.  The first | 
| +// line(s) report any code segments that are currently mapped in memory by | 
| +// reading the /proc/self/maps file and searching for "r-xp" access permission. | 
| +// The address of these sections are later used to symbolize the logged | 
| +// addresses of called functions.  The format of the /proc/self/maps file is: | 
| +//   00008000-0001f000 r-xp 00000000 b3:01 246        /system/bin/toolbox | 
| +//   0001f000-00021000 rw-p 00017000 b3:01 246        /system/bin/toolbox | 
| +//   00021000-00029000 rw-p 00000000 00:00 0          [heap] | 
| +//   40007000-40015000 r-xp 00000000 b3:01 574        /system/lib/libcutils.so | 
| +//   40015000-40016000 rw-p 0000e000 b3:01 574        /system/lib/libcutils.so | 
| +//   40016000-40025000 rw-p 00000000 00:00 0 | 
| +//   4002c000-4002f000 r-xp 00000000 b3:01 613        /system/lib/liblog.so | 
| +//   4002f000-40030000 rw-p 00003000 b3:01 613        /system/lib/liblog.so | 
| +// It also prints column headers and START on a new line to indicate that | 
| +// logging is starting.  If the name of the log file has not yet been set, it | 
| +// sets the name of the log file to the default name (i.e. kDefaultFileName). | 
| +void cygprofile_enable(void) { | 
| +  char line[kMaxLineSize]; | 
| +  void (*this_fn)(void) = &cygprofile_enable; | 
| +  char* p; | 
| +  char* q; | 
| +  std::ifstream mapsfile; | 
| + | 
| +  // Return if profiling is already enabled | 
| +  if (gCygProfileEnabled) | 
| +    return; | 
| + | 
| +  gFunctionsCalled = new std::set<void*>(); | 
| + | 
| +  if (!gCygProfileFileName[0]) | 
| +    cygprofile_setfilename(kDefaultFileName); | 
| +  if (!cygprofile_openlogfile(gCygProfileFileName)) | 
| +    return; | 
| + | 
| +  mapsfile.open("/proc/self/maps"); | 
| +  if (mapsfile.good()) { | 
| +    while (mapsfile.getline(line, kMaxLineSize)) { | 
| +      std::string str_line = std::string(line); | 
| +      int permindex = str_line.find("r-xp"); | 
| +      if (permindex != std::string::npos) { | 
| +        int dashindex = str_line.find("-"); | 
| +        int spaceindex = str_line.find(" "); | 
| +        void* start = reinterpret_cast<void*> | 
| +            (strtol((str_line.substr(0, dashindex)).c_str(), | 
| +                    &p, 16)); | 
| +        if (*p != 0) | 
| +          fprintf(gLogFile, | 
| +                  "cyg-profile.cc: ERROR: could not determine start: %s.\n", | 
| +                  (str_line.substr(0, dashindex)).c_str()); | 
| +        void* end = reinterpret_cast<void*> | 
| +            (strtol((str_line.substr(dashindex + 1, | 
| +                                     spaceindex - dashindex - 1)).c_str(), | 
| +                    &q, 16)); | 
| +        if (*q != 0) | 
| +          fprintf(gLogFile, | 
| +                  "cyg-profile.cc: ERROR: could not determine end: %s.\n", | 
| +                  (str_line.substr(dashindex + 1, | 
| +                                   spaceindex - dashindex - 1)).c_str()); | 
| +        if (this_fn >= start && this_fn < end) | 
| +          fprintf(gLogFile, "%s\n", str_line.c_str()); | 
| +      } | 
| +    } | 
| +  } else { | 
| +    fprintf(gLogFile, "cyg-profile.cc: ERROR: Can't open maps file."); | 
| +  } | 
| +  mapsfile.close(); | 
| +  fprintf(gLogFile, "secs       msecs\tpid:threadid\tfunc\nSTART\n"); | 
| +  gCygProfileEnabled = true; | 
| +} | 
| + | 
| +// Disables profiling. | 
| +void cygprofile_disable(void) { | 
| +  gCygProfileEnabled = false; | 
| +  pthread_mutex_lock(&gMutex); | 
| +  delete gFunctionsCalled; | 
| +  gFunctionsCalled = NULL; | 
| +  pthread_mutex_unlock(&gMutex); | 
| +} | 
| + | 
| +// Returns true if profiling is enabled. | 
| +bool cygprofile_isenabled(void) { | 
| +  return gCygProfileEnabled; | 
| +} | 
| + | 
| +// Sets the name of the log file.  Returns -1 without setting the name if | 
| +// profiling has already been enabled, because enabling profiling before | 
| +// setting the file name will set the name of the log file to the default | 
| +// name.  If the file name is greater than the maximum size (kMaxFileNameSizeSize) | 
| +// returns -2 without setting the file name. | 
| +int cygprofile_setfilename(const char* filename) { | 
| +  if (cygprofile_isenabled()) | 
| +    return -1; | 
| + | 
| +  if (strlen(filename) > kMaxFileNameSizeSize) | 
| +    return -2; | 
| + | 
| +  const char* ptr = strstr(filename, "%d"); | 
| +  if (ptr) { | 
| +    size_t len; | 
| +    len = ptr - filename; | 
| +    snprintf(gCygProfileFileName, len+1, "%s", filename); | 
| +    snprintf(&gCygProfileFileName[len], kMaxFileNameSizeSize - len, | 
| +             "%d", getpid()); | 
| +    len = strlen(gCygProfileFileName); | 
| +    snprintf(&gCygProfileFileName[len], kMaxFileNameSizeSize - len, | 
| +             "%s", ptr + 2); | 
| +  } else { | 
| +    snprintf(gCygProfileFileName, kMaxFileNameSizeSize, "%s", filename); | 
| +  } | 
| + | 
| +  if (gLogFile) | 
| +    cygprofile_closelogfile(); | 
| + | 
| +  return 0; | 
| +} | 
| + | 
| +// Returns the log file name.  If it has yet to be set, it is first | 
| +// set to the default file name, kDefaultFileName. | 
| +char* cygprofile_getfilename(void) { | 
| +  if (!strlen(gCygProfileFileName)) | 
| +    cygprofile_setfilename(kDefaultFileName); | 
| +  return gCygProfileFileName; | 
| +} | 
| + | 
| +// Opens the file name specified and returns a pointer to the file. | 
| +FILE* cygprofile_openlogfile(const char* filename) { | 
| +  static int complained = 0; | 
| +  FILE* file; | 
| + | 
| +  if (complained) | 
| +    return NULL; | 
| + | 
| +  if (gLogFile) | 
| +    return gLogFile; | 
| + | 
| +  file = fopen(filename, "w"); | 
| +  if (!file) { | 
| +    fprintf(stderr, "WARNING: Can't open gLogFile '%s': %s\n", | 
| +            filename, strerror(errno)); | 
| +    complained = 1; | 
| +    return NULL; | 
| +  } | 
| + | 
| +  setlinebuf(file); | 
| +  gLogFile = file; | 
| + | 
| +  return file; | 
| +} | 
| + | 
| +// Closes the log file (i.e. gLogFile). | 
| +void cygprofile_closelogfile(void) { | 
| +  if (gLogFile) | 
| +    fclose(gLogFile); | 
| +} | 
| + | 
| +// Given a file name, sets the name of the log file and then enables profiling. | 
| +void cygprofile_start(const char* filename) { | 
| +  cygprofile_setfilename(filename); | 
| +  cygprofile_enable(); | 
| +} | 
| + | 
| +// Indicates the end of profiling.  Disables profiling, writes "END" on a | 
| +// new line to indicate no more profiling, flushes the gLogFile and finally | 
| +// closes the log file. | 
| +void cygprofile_end(void) { | 
| +  cygprofile_disable(); | 
| +  fprintf(gLogFile, "END\n"); | 
| +  fflush(gLogFile); | 
| +  cygprofile_closelogfile(); | 
| +} | 
| + | 
| +}  // namespace cygprofile_startup | 
|  |