Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(879)

Side by Side Diff: tools/cygprofile/cygprofile_android.cc

Issue 10697079: Upstreaming Cygprofile for Android. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * cygprofile_android.cc - CygProfiler runtime functions.
3 *
4 * Michal Ludvig <michal@logix.cz>
5 * http://www.logix.cz/michal/devel
6 *
7 * cygprofile_android.cc
8 * - Compile your program with -finstrument-functions and link
9 * together with this code.
10 * - Logging is enabled as soon as your program calls
11 * cygprofile_enable() and disabled with cygprofile_disable().
12 * - Before logging was enabled you can change the name
13 * of a logfile by calling cygprofile_setfilename().
14 *
15 * This implementation has been modified in the following ways:
16 * 1) Upon profile enabling, the virtual address is logged as the
17 * first line to assist in symbolizing logged virutal addresses.
18 * 2) Only function entry for the first time a particular function
19 * is entered is logged. This is for profiling for code layout.
20 * 3) The implementation is made thread-safe.
21 * 4) The implementation logs the timestamp (seconds and microsecs).
22 * 5) The implementation adds function cygprofile_start(filename) which
23 * calls cyg_setfilename and enables profiling and cygprofile_end()
24 * to guarantee log buffer is flushed and logfile is closed.
25 */
26
27 #include <errno.h>
28 #include <pthread.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <sys/time.h>
33 #include <time.h>
34 #include <unistd.h>
35
36 #include <fstream>
37 #include <set>
38
39 #include "cygprofile_android.h"
40
41 namespace cygprofile {
42
43 const int kMaxFileNameSizeSize = 100;
44
45 // On Android, put the default location for the profile files on the
46 // SD Card partition for easy retrieval and ensuring a fixed absolute
47 // paths for all output.
48 #ifdef __ANDROID__
49 static const char kDefaultFileName[] = "/sdcard/cyglog.%d";
50 #else
51 const char* kDefaultFileName = "cyglog.%d";
52 #endif
53 const int kMaxLineSize = 512;
54
55 static FILE* gLogFile = NULL;
56 static volatile bool gCygProfileEnabled = false;
57 static char gCygProfileFileName[kMaxFileNameSizeSize + 1];
58
59 // Keeps track of all functions that have been logged.
60 static std::set<void*>* gFunctionsCalled = NULL;
61
62
63 // Return the current timestamp in seconds and micro-seconds
64 // On Linux-based systems, use the raw monotonic clock to ensure that
65 // the timestamps are not affected by NTP adjustments. Otherwise simply
66 // use gettimeofday()
67 #ifdef __linux__
68
69 // We use CLOCK_MONOTONIC_RAW on Linux, because CLOCK_MONOTONIC is broken
70 // and is subject to NTP adjustments since Linux 2.6.28. This bug has been
71 // fixed in 2.6.32 which not all devices run yet.
72 //
73 // See http://stackoverflow.com/questions/3657289/linux-clock-gettimeclock-monot onic-strange-non-monotonic-behavior
74 //
75
76 // The Android Bionic headers don't necessarily define the constant.
77 #ifndef CLOCK_MONOTONIC_RAW
78 #define CLOCK_MONOTONIC_RAW 4
79 #endif
80
81 static void
82 get_timestamp(time_t *seconds, long *usecs)
83 {
84 struct timespec ts;
85 clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
86 *seconds = ts.tv_sec;
87 *usecs = ts.tv_nsec / 1000;
88 }
89
90 #else // !__linux__
91
92 static void
93 get_timestamp(time_t *seconds, long *usecs)
94 {
95 struct timeval tv;
96 gettimeofday(&tv, NULL);
97 *seconds = tv.tv_sec;
98 *usecs = tv.tv_usec;
99 }
100
101 #endif // !__linux__
102
103 #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER
104 # ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
105 # define PTHREAD_RECURSIVE_MUTEX_INITIALIZER PTHREAD_RECURSIVE_MUTEX_INITIALI ZER_NP
106 # else
107 # error MISSING PTHREAD_RECURSIVE_MUTEX_INITIALIZER DEFINITION!
108 # endif
109 #endif
110
111 // Ensure thread safety (see __cyg_profile_func_enter)
112 static pthread_mutex_t gMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
113 static unsigned int gDepth = 0;
114
115 #ifdef __cplusplus
116 extern "C" {
117 #endif
118
119 FILE* cygprofile_openlogfile(const char* filename)
120 __attribute__((no_instrument_function));
121 void cygprofile_closelogfile(void)
122 __attribute__((no_instrument_function));
123
124 // Note that these are linked internally by the compiler.
125 // Don't call them directly!
126 void __cyg_profile_func_enter(void* this_fn, void* call_site)
127 __attribute__((no_instrument_function));
128 void __cyg_profile_func_exit(void* this_fn, void* call_site)
129 __attribute__((no_instrument_function));
130
131 #ifdef __cplusplus
132 };
133 #endif
134
135 // Called internally by instrumentation inserted by the compiler upon entering
136 // a function. Instrumentation is inserted by the compiler for all functions
137 // whose source is compiled with the -finstrument-functions CFLAG. The code
138 // logs the call along with the timestamp, process id and thread id.
139 void __cyg_profile_func_enter(void* this_fn, void* call_site) {
140 pthread_mutex_lock(&gMutex);
141 // avoid recursive calls
142 if (gDepth == 0) {
143 gDepth += 1;
144 if (!gCygProfileEnabled) {
145 cygprofile_enable();
146 }
147 if (gCygProfileEnabled && gLogFile) {
148 // gFunctionsCalled is initialized in cygprofile_enable()
149 if (gFunctionsCalled && gFunctionsCalled->find(this_fn) ==
150 gFunctionsCalled->end()) {
151 time_t seconds;
152 long usecs;
153 get_timestamp(&seconds, &usecs);
154 fprintf(gLogFile, "%ld %ld\t%d:%ld\t%p\n",
155 seconds, usecs, getpid(), pthread_self(), this_fn);
156 fflush(gLogFile);
157 gFunctionsCalled->insert(this_fn);
158 }
159 }
160 gDepth -= 1;
161 }
162 pthread_mutex_unlock(&gMutex);
163 }
164
165 // Called internally by instrumentation inserted by the compiler upon exiting
166 // a function. Instrumentation is inserted by the compiler for all functions
167 // whose source is compiled with the -finstrument-functions CFLAG. The exit
168 // of a function is ignored because only enter is useful for order profiling.
169 void __cyg_profile_func_exit(void* this_fn, void* call_site) {
170 // Do not do anything on function exit.
171 }
172
173 // Enables profiling and writes the first lines to the log file. The first
174 // line(s) report any code segments that are currently mapped in memory by
175 // reading the /proc/self/maps file and searching for "r-xp" access permission.
176 // The address of these sections are later used to symbolize the logged
177 // addresses of called functions. The format of the /proc/self/maps file is:
178 // 00008000-0001f000 r-xp 00000000 b3:01 246 /system/bin/toolbox
179 // 0001f000-00021000 rw-p 00017000 b3:01 246 /system/bin/toolbox
180 // 00021000-00029000 rw-p 00000000 00:00 0 [heap]
181 // 40007000-40015000 r-xp 00000000 b3:01 574 /system/lib/libcutils.so
182 // 40015000-40016000 rw-p 0000e000 b3:01 574 /system/lib/libcutils.so
183 // 40016000-40025000 rw-p 00000000 00:00 0
184 // 4002c000-4002f000 r-xp 00000000 b3:01 613 /system/lib/liblog.so
185 // 4002f000-40030000 rw-p 00003000 b3:01 613 /system/lib/liblog.so
186 // It also prints column headers and START on a new line to indicate that
187 // logging is starting. If the name of the log file has not yet been set, it
188 // sets the name of the log file to the default name (i.e. kDefaultFileName).
189 void cygprofile_enable(void) {
190 char line[kMaxLineSize];
191 void (*this_fn)(void) = &cygprofile_enable;
192 char* p;
193 char* q;
194 std::ifstream mapsfile;
195
196 // Return if profiling is already enabled
197 if (gCygProfileEnabled)
198 return;
199
200 gFunctionsCalled = new std::set<void*>();
201
202 if (!gCygProfileFileName[0])
203 cygprofile_setfilename(kDefaultFileName);
204 if (!cygprofile_openlogfile(gCygProfileFileName))
205 return;
206
207 mapsfile.open("/proc/self/maps");
208 if (mapsfile.good()) {
209 while (mapsfile.getline(line, kMaxLineSize)) {
210 std::string str_line = std::string(line);
211 int permindex = str_line.find("r-xp");
212 if (permindex != std::string::npos) {
213 int dashindex = str_line.find("-");
214 int spaceindex = str_line.find(" ");
215 void* start = reinterpret_cast<void*>
216 (strtol((str_line.substr(0, dashindex)).c_str(),
217 &p, 16));
218 if (*p != 0)
219 fprintf(gLogFile,
220 "cyg-profile.cc: ERROR: could not determine start: %s.\n",
221 (str_line.substr(0, dashindex)).c_str());
222 void* end = reinterpret_cast<void*>
223 (strtol((str_line.substr(dashindex + 1,
224 spaceindex - dashindex - 1)).c_str(),
225 &q, 16));
226 if (*q != 0)
227 fprintf(gLogFile,
228 "cyg-profile.cc: ERROR: could not determine end: %s.\n",
229 (str_line.substr(dashindex + 1,
230 spaceindex - dashindex - 1)).c_str());
231 if (this_fn >= start && this_fn < end)
232 fprintf(gLogFile, "%s\n", str_line.c_str());
233 }
234 }
235 } else {
236 fprintf(gLogFile, "cyg-profile.cc: ERROR: Can't open maps file.");
237 }
238 mapsfile.close();
239 fprintf(gLogFile, "secs msecs\tpid:threadid\tfunc\nSTART\n");
240 gCygProfileEnabled = true;
241 }
242
243 // Disables profiling.
244 void cygprofile_disable(void) {
245 gCygProfileEnabled = false;
246 pthread_mutex_lock(&gMutex);
247 delete gFunctionsCalled;
248 gFunctionsCalled = NULL;
249 pthread_mutex_unlock(&gMutex);
250 }
251
252 // Returns true if profiling is enabled.
253 bool cygprofile_isenabled(void) {
254 return gCygProfileEnabled;
255 }
256
257 // Sets the name of the log file. Returns -1 without setting the name if
258 // profiling has already been enabled, because enabling profiling before
259 // setting the file name will set the name of the log file to the default
260 // name. If the file name is greater than the maximum size (kMaxFileNameSizeSiz e)
261 // returns -2 without setting the file name.
262 int cygprofile_setfilename(const char* filename) {
263 if (cygprofile_isenabled())
264 return -1;
265
266 if (strlen(filename) > kMaxFileNameSizeSize)
267 return -2;
268
269 const char* ptr = strstr(filename, "%d");
270 if (ptr) {
271 size_t len;
272 len = ptr - filename;
273 snprintf(gCygProfileFileName, len+1, "%s", filename);
274 snprintf(&gCygProfileFileName[len], kMaxFileNameSizeSize - len,
275 "%d", getpid());
276 len = strlen(gCygProfileFileName);
277 snprintf(&gCygProfileFileName[len], kMaxFileNameSizeSize - len,
278 "%s", ptr + 2);
279 } else {
280 snprintf(gCygProfileFileName, kMaxFileNameSizeSize, "%s", filename);
281 }
282
283 if (gLogFile)
284 cygprofile_closelogfile();
285
286 return 0;
287 }
288
289 // Returns the log file name. If it has yet to be set, it is first
290 // set to the default file name, kDefaultFileName.
291 char* cygprofile_getfilename(void) {
292 if (!strlen(gCygProfileFileName))
293 cygprofile_setfilename(kDefaultFileName);
294 return gCygProfileFileName;
295 }
296
297 // Opens the file name specified and returns a pointer to the file.
298 FILE* cygprofile_openlogfile(const char* filename) {
299 static int complained = 0;
300 FILE* file;
301
302 if (complained)
303 return NULL;
304
305 if (gLogFile)
306 return gLogFile;
307
308 file = fopen(filename, "w");
309 if (!file) {
310 fprintf(stderr, "WARNING: Can't open gLogFile '%s': %s\n",
311 filename, strerror(errno));
312 complained = 1;
313 return NULL;
314 }
315
316 setlinebuf(file);
317 gLogFile = file;
318
319 return file;
320 }
321
322 // Closes the log file (i.e. gLogFile).
323 void cygprofile_closelogfile(void) {
324 if (gLogFile)
325 fclose(gLogFile);
326 }
327
328 // Given a file name, sets the name of the log file and then enables profiling.
329 void cygprofile_start(const char* filename) {
330 cygprofile_setfilename(filename);
331 cygprofile_enable();
332 }
333
334 // Indicates the end of profiling. Disables profiling, writes "END" on a
335 // new line to indicate no more profiling, flushes the gLogFile and finally
336 // closes the log file.
337 void cygprofile_end(void) {
338 cygprofile_disable();
339 fprintf(gLogFile, "END\n");
340 fflush(gLogFile);
341 cygprofile_closelogfile();
342 }
343
344 } // namespace cygprofile
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698