summaryrefslogtreecommitdiffstats
path: root/tools/cygprofile/cygprofile.h
blob: 4ff13766bfd42ba1bac966af9694eada4a8539e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Tool to log the execution of the process (Chrome). Writes logs containing
// time and address of the callback being called for the first time.
//
// For performance reasons logs are buffered. Every thread has its own buffer
// and log file so the contention between threads is minimal. As a side-effect,
// functions called might be mentioned in many thread logs.
//
// A special thread is created in the process to periodically flush logs for all
// threads in case the thread had stopped before flushing its logs.
//
// Also note that the instrumentation code is self-activated. It begins to
// record the log data when it is called first, including the run-time startup.
// Have it in mind when modifying it, in particular do not use global objects
// with constructors as they are called during startup (too late for us).

#ifndef TOOLS_CYGPROFILE_CYGPROFILE_H_
#define TOOLS_CYGPROFILE_CYGPROFILE_H_

#include <vector>

#include <sys/time.h>
#include <sys/types.h>

#include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"

#if !defined(OS_ANDROID)
// This is only supported on Android thanks to the fact that on Android
// processes (other than the system's zygote) don't fork.
//
// To make cygprofile truly work (i.e. without any deadlock) on Chrome
// platforms that use fork(), cygprofile.cc should be written in a way that
// guarantees that:
// - No lock is acquired by a foreign thread during fork(). In particular this
// means that cygprofile.cc should not perform any heap allocation (since heap
// allocators, including TCMalloc generally use locks).
// - Only cygprofile.cc uses pthread_atfork() in the whole process. Unlike POSIX
// signals, pthread_atfork() doesn't provide a way to install multiple handlers.
// Calling pthread_atfork() in cygprofile.cc would override any handler that
// could have been installed previously.
//
// Chrome happens to violate the first requirement at least once by having its
// process launcher thread fork. However the child process in that case, when
// it's not instrumented with cygprofile, directly calls exec(). This is safe
// since the child process doesn't try to release a lock acquired by another
// thread in the parent process which would lead to a deadlock. This problem was
// actually observed by trying to port the current version of cygprofile.cc to
// Linux.
#error This is only supported on Android.
#endif

// The following is only exposed for testing.
namespace cygprofile {

class Thread;

// Single log entry recorded for each function call.
struct LogEntry {
  LogEntry(const void* address);

  const timespec time;
  const pid_t pid;
  const pid_t tid;
  const void* const address;
};

// Per-thread function calls log.
class ThreadLog {
 public:
  // Callback invoked for flushing that can be provided for testing.
  typedef base::Callback<void (std::vector<LogEntry>*)> FlushCallback;

  ThreadLog();

  // Used for testing.
  ThreadLog(const FlushCallback& flush_callback);

  ~ThreadLog();

  // Must only be called from the thread this ThreadLog instance is watching.
  void AddEntry(void* address);

  // Can be called from any thread.
  void TakeEntries(std::vector<LogEntry>* output);

  // Flushes the provided vector of entries to a file and clears it. Note that
  // this can be called from any thread.
  void Flush(std::vector<LogEntry>* entries) const;

 private:
  // Default implementation (that can be overridden for testing) of the method
  // above.
  void FlushInternal(std::vector<LogEntry>* entries) const;

  // Thread identifier as Linux kernel shows it.  LWP (light-weight process) is
  // a unique ID of the thread in the system, unlike pthread_self() which is the
  // same for fork()-ed threads.
  const pid_t tid_;

  // Current thread is inside the instrumentation routine.
  bool in_use_;

  // Callback used to flush entries.
  const FlushCallback flush_callback_;

  // Keeps track of all functions that have been logged on this thread so we do
  // not record duplicates.
  base::hash_set<void*> called_functions_;

  // A lock that guards |entries_| usage between per-thread instrumentation
  // routine and timer flush callback. So the contention could happen only
  // during the flush, every 15 secs.
  base::Lock lock_;

  std::vector<LogEntry> entries_;

  DISALLOW_COPY_AND_ASSIGN(ThreadLog);
};

// Manages a list of per-thread logs.
class ThreadLogsManager {
 public:
  ThreadLogsManager();

  // Used for testing. The provided callbacks are used for testing to
  // synchronize the internal thread with the unit test running on the main
  // thread.
  ThreadLogsManager(const base::Closure& wait_callback,
                    const base::Closure& notify_callback);

  ~ThreadLogsManager();

  // Can be called from any thread.
  void AddLog(scoped_ptr<ThreadLog> new_log);

 private:
  void StartInternalFlushThread_Locked();

  // Flush thread's entry point.
  void FlushAllLogsOnFlushThread();

  // Used to make the internal thread sleep before each flush iteration.
  const base::Closure wait_callback_;
  // Used to trigger a notification when a flush happened on the internal
  // thread.
  const base::Closure notify_callback_;

  // Protects the state below.
  base::Lock lock_;
  scoped_ptr<Thread> flush_thread_;
  std::vector<ThreadLog*> logs_;

  DISALLOW_COPY_AND_ASSIGN(ThreadLogsManager);
};

}  // namespace cygprofile

#endif  // TOOLS_CYGPROFILE_CYGPROFILE_H_