1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Tool to log the execution of the process (Chrome). Writes logs containing
6 // time and address of the callback being called for the first time.
8 // To speed up the logging, buffering logs is implemented. Every thread have its
9 // own buffer and log file so the contention between threads is minimal. As a
10 // side-effect, functions called might be mentioned in many thread logs.
12 // Special thread is created in the process to periodically flushes logs for all
13 // threads for the case the thread has stopped before flushing its logs.
15 // Use this profiler with linux_use_tcmalloc=0.
17 // Note for the ChromeOS Chrome. Remove renderer process from the sandbox (add
18 // --no-sandbox option to running Chrome in /sbin/session_manager_setup.sh).
19 // Otherwise renderer will not be able to write logs (and will assert on that).
21 // Also note that the instrumentation code is self-activated. It begins to
22 // record the log data when it is called first, including the run-time startup.
23 // Have it in mind when modifying it, in particular do not use global objects
24 // with constructors as they are called during startup (too late for us).
32 #include <sys/syscall.h>
34 #include <sys/types.h>
37 #include "base/containers/hash_tables.h"
38 #include "base/lazy_instance.h"
39 #include "base/logging.h"
40 #include "base/memory/singleton.h"
41 #include "base/synchronization/lock.h"
43 namespace cygprofile
{
47 // Note that these are linked internally by the compiler. Don't call
49 void __cyg_profile_func_enter(void* this_fn
, void* call_site
)
50 __attribute__((no_instrument_function
));
51 void __cyg_profile_func_exit(void* this_fn
, void* call_site
)
52 __attribute__((no_instrument_function
));
56 // Single log entry layout.
63 CygLogEntry(time_t seconds
, long int usec
,
64 pid_t pid
, pthread_t tid
, const void* this_fn
)
65 : seconds(seconds
), usec(usec
),
66 pid(pid
), tid(tid
), this_fn(this_fn
) {}
69 // Common data for the process. Singleton.
72 static CygCommon
* GetInstance();
73 std::string
header() const { return header_line_
; }
76 std::string header_line_
;
77 friend struct DefaultSingletonTraits
<CygCommon
>;
79 DISALLOW_COPY_AND_ASSIGN(CygCommon
);
82 // Returns light-weight process ID. On linux, this is a system-wide
84 static pid_t
GetLwp() {
85 return syscall(__NR_gettid
);
88 // A per-thread structure representing the log itself.
92 : in_use_(false), lwp_(GetLwp()), pthread_self_(pthread_self()) { }
94 // Enter a log entity.
95 void LogEnter(void* this_fn
);
97 // Add newly created CygTlsLog object to the list of all such objects.
98 // Needed for the timer callback: it will enumerate each object and flush.
99 static void AddNewLog(CygTlsLog
* newlog
);
101 // Starts a thread in this process that periodically flushes all the threads.
102 // Must be called once per process.
103 static void StartFlushLogThread();
106 static const int kBufMaxSize
;
107 static const char kLogFilenameFmt
[];
108 static const char kLogFileNamePrefix
[];
110 // Flush the log to file. Create file if needed.
111 // Must be called with locked log_mutex_.
114 // Fork hooks. Needed to keep data in consistent state during fork().
115 static void AtForkPrepare();
116 static void AtForkParent();
117 static void AtForkChild();
119 // Thread callback to flush all logs periodically.
120 static void* FlushLogThread(void*);
122 std::string log_filename_
;
123 std::vector
<CygLogEntry
> buf_
;
125 // A lock that guards buf_ usage between per-thread instrumentation
126 // routine and timer flush callback. So the contention could happen
127 // only during the flush, every 30 secs.
128 base::Lock log_mutex_
;
130 // Current thread is inside the instrumentation routine.
133 // Keeps track of all functions that have been logged on this thread
134 // so we do not record dublicates.
135 std::hash_set
<void*> functions_called_
;
137 // Thread identifier as Linux kernel shows it. For debugging purposes.
138 // LWP (light-weight process) is a unique ID of the thread in the system,
139 // unlike pthread_self() which is the same for fork()-ed threads.
141 pthread_t pthread_self_
;
143 DISALLOW_COPY_AND_ASSIGN(CygTlsLog
);
146 // Storage for logs for all threads in the process.
147 // Using std::list may be better, but it fails when used before main().
149 std::vector
<CygTlsLog
*> logs
;
153 base::LazyInstance
<AllLogs
>::Leaky all_logs_
= LAZY_INSTANCE_INITIALIZER
;
155 // Per-thread pointer to the current log object.
156 static __thread CygTlsLog
* tls_current_log
= NULL
;
158 // Magic value of above to prevent the instrumentation. Used when CygTlsLog is
159 // being constructed (to prevent reentering by malloc, for example) and by
160 // the FlushLogThread (to prevent it being logged - see comment in its code).
161 CygTlsLog
* const kMagicBeingConstructed
= reinterpret_cast<CygTlsLog
*>(1);
163 // Number of entries in the per-thread log buffer before we flush.
164 // Note, that we also flush by timer so not all thread logs may grow up to this.
165 const int CygTlsLog::kBufMaxSize
= 3000;
167 #if defined(OS_ANDROID)
168 const char CygTlsLog::kLogFileNamePrefix
[] =
169 "/data/local/tmp/chrome/cyglog/";
171 const char CygTlsLog::kLogFileNamePrefix
[] = "/var/log/chrome/";
174 // "cyglog.PID.LWP.pthread_self.PPID"
175 const char CygTlsLog::kLogFilenameFmt
[] = "%scyglog.%d.%d.%ld-%d";
177 CygCommon
* CygCommon::GetInstance() {
178 return Singleton
<CygCommon
>::get();
181 CygCommon::CygCommon() {
182 // Determine our module addresses.
183 std::ifstream
mapsfile("/proc/self/maps");
184 CHECK(mapsfile
.good());
185 static const int kMaxLineSize
= 512;
186 char line
[kMaxLineSize
];
187 void (*this_fn
)(void) =
188 reinterpret_cast<void(*)()>(__cyg_profile_func_enter
);
189 while (mapsfile
.getline(line
, kMaxLineSize
)) {
190 const std::string str_line
= line
;
191 size_t permindex
= str_line
.find("r-xp");
192 if (permindex
!= std::string::npos
) {
193 int dashindex
= str_line
.find("-");
194 int spaceindex
= str_line
.find(" ");
196 void* start
= reinterpret_cast<void*>
197 (strtol((str_line
.substr(0, dashindex
)).c_str(),
199 CHECK(*p
== 0); // Could not determine start address.
200 void* end
= reinterpret_cast<void*>
201 (strtol((str_line
.substr(dashindex
+ 1,
202 spaceindex
- dashindex
- 1)).c_str(),
204 CHECK(*p
== 0); // Could not determine end address.
206 if (this_fn
>= start
&& this_fn
< end
)
207 header_line_
= str_line
;
211 header_line_
.append("\nsecs\tmsecs\tpid:threadid\tfunc\n");
214 void CygTlsLog::LogEnter(void* this_fn
) {
219 if (functions_called_
.find(this_fn
) ==
220 functions_called_
.end()) {
221 functions_called_
.insert(this_fn
);
222 base::AutoLock
lock(log_mutex_
);
223 if (buf_
.capacity() < kBufMaxSize
)
224 buf_
.reserve(kBufMaxSize
);
225 struct timeval timestamp
;
226 gettimeofday(×tamp
, NULL
);
227 buf_
.push_back(CygLogEntry(time(NULL
), timestamp
.tv_usec
,
228 getpid(), pthread_self(), this_fn
));
229 if (buf_
.size() == kBufMaxSize
) {
237 void CygTlsLog::AtForkPrepare() {
238 CHECK(tls_current_log
);
239 CHECK(tls_current_log
->lwp_
== GetLwp());
240 CHECK(tls_current_log
->pthread_self_
== pthread_self());
241 all_logs_
.Get().mutex
.Acquire();
244 void CygTlsLog::AtForkParent() {
245 CHECK(tls_current_log
);
246 CHECK(tls_current_log
->lwp_
== GetLwp());
247 CHECK(tls_current_log
->pthread_self_
== pthread_self());
248 all_logs_
.Get().mutex
.Release();
251 void CygTlsLog::AtForkChild() {
252 CHECK(tls_current_log
);
254 // Update the IDs of this new thread of the new process.
255 // Note that the process may (and Chrome main process forks zygote this way)
256 // call exec(self) after we return (to launch new shiny self). If done like
257 // that, PID and LWP will remain the same, but pthread_self() changes.
258 pid_t lwp
= GetLwp();
259 CHECK(tls_current_log
->lwp_
!= lwp
); // LWP is system-wide unique thread ID.
260 tls_current_log
->lwp_
= lwp
;
262 CHECK(tls_current_log
->pthread_self_
== pthread_self());
264 // Leave the only current thread tls object because fork() clones only the
265 // current thread (the one that called fork) to the child process.
266 AllLogs
& all_logs
= all_logs_
.Get();
267 all_logs
.logs
.clear();
268 all_logs
.logs
.push_back(tls_current_log
);
269 CHECK(all_logs
.logs
.size() == 1);
271 // Clear log filename so it will be re-calculated with the new PIDs.
272 tls_current_log
->log_filename_
.clear();
274 // Create the thread that will periodically flush all logs for this process.
275 StartFlushLogThread();
277 // We do not update log header line (CygCommon data) as it will be the same
278 // because the new process is just a forked copy.
279 all_logs
.mutex
.Release();
282 void CygTlsLog::StartFlushLogThread() {
284 CHECK(!pthread_create(&tid
, NULL
, &CygTlsLog::FlushLogThread
, NULL
));
287 void CygTlsLog::AddNewLog(CygTlsLog
* newlog
) {
288 CHECK(tls_current_log
== kMagicBeingConstructed
);
289 AllLogs
& all_logs
= all_logs_
.Get();
290 base::AutoLock
lock(all_logs
.mutex
);
291 if (all_logs
.logs
.empty()) {
293 // An Android app never fork, it always starts with a pre-defined number of
294 // process descibed by the android manifest file. In fact, there is not
295 // support for pthread_atfork at the android system libraries. All chrome
296 // for android processes will start as independent processs and each one
297 // will generate its own logs that will later have to be merged as usual.
298 #if !defined(OS_ANDROID)
299 CHECK(!pthread_atfork(CygTlsLog::AtForkPrepare
,
300 CygTlsLog::AtForkParent
,
301 CygTlsLog::AtForkChild
));
304 // The very first process starts its flush thread here. Forked processes
305 // will do it in AtForkChild().
306 StartFlushLogThread();
308 all_logs
.logs
.push_back(newlog
);
311 // Printf-style routine to write to open file.
312 static void WriteLogLine(int fd
, const char* fmt
, ...) {
314 va_start(arg_ptr
, fmt
);
316 int len
= vsnprintf(msg
, sizeof(msg
), fmt
, arg_ptr
);
317 int rc
= write(fd
, msg
, (len
> sizeof(msg
))? sizeof(msg
): len
);
321 void CygTlsLog::FlushLog() {
322 bool first_log_write
= false;
323 if (log_filename_
.empty()) {
324 first_log_write
= true;
326 snprintf(buf
, sizeof(buf
), kLogFilenameFmt
,
327 kLogFileNamePrefix
, getpid(), lwp_
, pthread_self_
, getppid());
329 unlink(log_filename_
.c_str());
332 int file
= open(log_filename_
.c_str(), O_CREAT
| O_WRONLY
| O_APPEND
, 00600);
336 WriteLogLine(file
, "%s", CygCommon::GetInstance()->header().c_str());
338 for (int i
= 0; i
!= buf_
.size(); ++i
) {
339 const CygLogEntry
& p
= buf_
[i
];
340 WriteLogLine(file
, "%ld %ld\t%d:%ld\t%p\n",
341 p
.seconds
, p
.usec
, p
.pid
, p
.tid
, p
.this_fn
);
348 void* CygTlsLog::FlushLogThread(void*) {
349 // Disable logging this thread. Although this routine is not instrumented
350 // (cygprofile.gyp provides that), the called routines are and thus will
351 // call instrumentation.
352 CHECK(tls_current_log
== NULL
); // Must be 0 as this is a new thread.
353 tls_current_log
= kMagicBeingConstructed
;
355 // Run this loop infinitely: sleep 30 secs and the flush all thread's
356 // buffers. There is a danger that, when quitting Chrome, this thread may
357 // see unallocated data and segfault. We do not care because we need logs
358 // when Chrome is working.
360 for(int secs_to_sleep
= 30; secs_to_sleep
!= 0;)
361 secs_to_sleep
= sleep(secs_to_sleep
);
363 AllLogs
& all_logs
= all_logs_
.Get();
364 base::AutoLock
lock(all_logs
.mutex
);
365 for (int i
= 0; i
!= all_logs
.logs
.size(); ++i
) {
366 CygTlsLog
* current_log
= all_logs
.logs
[i
];
367 base::AutoLock
current_lock(current_log
->log_mutex_
);
368 if (current_log
->buf_
.size()) {
369 current_log
->FlushLog();
371 // The thread's log is still empty. Probably the thread finished prior
372 // to previous timer fired - deallocate its buffer. Even if the thread
373 // ever resumes, it will allocate its buffer again in
374 // std::vector::push_back().
375 current_log
->buf_
.clear();
381 // Gcc Compiler callback, called on every function invocation providing
382 // addresses of caller and callee codes.
383 void __cyg_profile_func_enter(void* this_fn
, void* callee_unused
) {
384 if (tls_current_log
== NULL
) {
385 tls_current_log
= kMagicBeingConstructed
;
386 CygTlsLog
* newlog
= new CygTlsLog
;
388 CygTlsLog::AddNewLog(newlog
);
389 tls_current_log
= newlog
;
391 if (tls_current_log
!= kMagicBeingConstructed
) {
392 tls_current_log
->LogEnter(this_fn
);
396 // Gcc Compiler callback, called after every function invocation providing
397 // addresses of caller and callee codes.
398 void __cyg_profile_func_exit(void* this_fn
, void* call_site
) {
401 } // namespace cygprofile