Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / tcmalloc / vendor / src / profiledata.cc
blob5f2531bbb0079ca97917b6f04bc572d33cefe8d9
1 // Copyright (c) 2007, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 // ---
31 // Author: Sanjay Ghemawat
32 // Chris Demetriou (refactoring)
34 // Collect profiling data.
36 #include <config.h>
37 #include <assert.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <errno.h>
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #include <sys/time.h>
45 #include <string.h>
46 #include <fcntl.h>
48 #include "profiledata.h"
50 #include "base/logging.h"
51 #include "base/sysinfo.h"
53 // All of these are initialized in profiledata.h.
54 const int ProfileData::kMaxStackDepth;
55 const int ProfileData::kAssociativity;
56 const int ProfileData::kBuckets;
57 const int ProfileData::kBufferLength;
59 ProfileData::Options::Options()
60 : frequency_(1) {
63 // This function is safe to call from asynchronous signals (but is not
64 // re-entrant). However, that's not part of its public interface.
65 void ProfileData::Evict(const Entry& entry) {
66 const int d = entry.depth;
67 const int nslots = d + 2; // Number of slots needed in eviction buffer
68 if (num_evicted_ + nslots > kBufferLength) {
69 FlushEvicted();
70 assert(num_evicted_ == 0);
71 assert(nslots <= kBufferLength);
73 evict_[num_evicted_++] = entry.count;
74 evict_[num_evicted_++] = d;
75 memcpy(&evict_[num_evicted_], entry.stack, d * sizeof(Slot));
76 num_evicted_ += d;
79 ProfileData::ProfileData()
80 : hash_(0),
81 evict_(0),
82 num_evicted_(0),
83 out_(-1),
84 count_(0),
85 evictions_(0),
86 total_bytes_(0),
87 fname_(0),
88 start_time_(0) {
91 bool ProfileData::Start(const char* fname,
92 const ProfileData::Options& options) {
93 if (enabled()) {
94 return false;
97 // Open output file and initialize various data structures
98 int fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0666);
99 if (fd < 0) {
100 // Can't open outfile for write
101 return false;
104 start_time_ = time(NULL);
105 fname_ = strdup(fname);
107 // Reset counters
108 num_evicted_ = 0;
109 count_ = 0;
110 evictions_ = 0;
111 total_bytes_ = 0;
113 hash_ = new Bucket[kBuckets];
114 evict_ = new Slot[kBufferLength];
115 memset(hash_, 0, sizeof(hash_[0]) * kBuckets);
117 // Record special entries
118 evict_[num_evicted_++] = 0; // count for header
119 evict_[num_evicted_++] = 3; // depth for header
120 evict_[num_evicted_++] = 0; // Version number
121 CHECK_NE(0, options.frequency());
122 int period = 1000000 / options.frequency();
123 evict_[num_evicted_++] = period; // Period (microseconds)
124 evict_[num_evicted_++] = 0; // Padding
126 out_ = fd;
128 return true;
131 ProfileData::~ProfileData() {
132 Stop();
135 // Dump /proc/maps data to fd. Copied from heap-profile-table.cc.
136 #define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
138 static void FDWrite(int fd, const char* buf, size_t len) {
139 while (len > 0) {
140 ssize_t r;
141 NO_INTR(r = write(fd, buf, len));
142 RAW_CHECK(r >= 0, "write failed");
143 buf += r;
144 len -= r;
148 static void DumpProcSelfMaps(int fd) {
149 ProcMapsIterator::Buffer iterbuf;
150 ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
152 uint64 start, end, offset;
153 int64 inode;
154 char *flags, *filename;
155 ProcMapsIterator::Buffer linebuf;
156 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
157 int written = it.FormatLine(linebuf.buf_, sizeof(linebuf.buf_),
158 start, end, flags, offset, inode, filename,
160 FDWrite(fd, linebuf.buf_, written);
164 void ProfileData::Stop() {
165 if (!enabled()) {
166 return;
169 // Move data from hash table to eviction buffer
170 for (int b = 0; b < kBuckets; b++) {
171 Bucket* bucket = &hash_[b];
172 for (int a = 0; a < kAssociativity; a++) {
173 if (bucket->entry[a].count > 0) {
174 Evict(bucket->entry[a]);
179 if (num_evicted_ + 3 > kBufferLength) {
180 // Ensure there is enough room for end of data marker
181 FlushEvicted();
184 // Write end of data marker
185 evict_[num_evicted_++] = 0; // count
186 evict_[num_evicted_++] = 1; // depth
187 evict_[num_evicted_++] = 0; // end of data marker
188 FlushEvicted();
190 // Dump "/proc/self/maps" so we get list of mapped shared libraries
191 DumpProcSelfMaps(out_);
193 Reset();
194 fprintf(stderr, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS "\n",
195 count_, evictions_, total_bytes_);
198 void ProfileData::Reset() {
199 if (!enabled()) {
200 return;
203 // Don't reset count_, evictions_, or total_bytes_ here. They're used
204 // by Stop to print information about the profile after reset, and are
205 // cleared by Start when starting a new profile.
206 close(out_);
207 delete[] hash_;
208 hash_ = 0;
209 delete[] evict_;
210 evict_ = 0;
211 num_evicted_ = 0;
212 free(fname_);
213 fname_ = 0;
214 start_time_ = 0;
216 out_ = -1;
219 // This function is safe to call from asynchronous signals (but is not
220 // re-entrant). However, that's not part of its public interface.
221 void ProfileData::GetCurrentState(State* state) const {
222 if (enabled()) {
223 state->enabled = true;
224 state->start_time = start_time_;
225 state->samples_gathered = count_;
226 int buf_size = sizeof(state->profile_name);
227 strncpy(state->profile_name, fname_, buf_size);
228 state->profile_name[buf_size-1] = '\0';
229 } else {
230 state->enabled = false;
231 state->start_time = 0;
232 state->samples_gathered = 0;
233 state->profile_name[0] = '\0';
237 // This function is safe to call from asynchronous signals (but is not
238 // re-entrant). However, that's not part of its public interface.
239 void ProfileData::FlushTable() {
240 if (!enabled()) {
241 return;
244 // Move data from hash table to eviction buffer
245 for (int b = 0; b < kBuckets; b++) {
246 Bucket* bucket = &hash_[b];
247 for (int a = 0; a < kAssociativity; a++) {
248 if (bucket->entry[a].count > 0) {
249 Evict(bucket->entry[a]);
250 bucket->entry[a].depth = 0;
251 bucket->entry[a].count = 0;
256 // Write out all pending data
257 FlushEvicted();
260 void ProfileData::Add(int depth, const void* const* stack) {
261 if (!enabled()) {
262 return;
265 if (depth > kMaxStackDepth) depth = kMaxStackDepth;
266 RAW_CHECK(depth > 0, "ProfileData::Add depth <= 0");
268 // Make hash-value
269 Slot h = 0;
270 for (int i = 0; i < depth; i++) {
271 Slot slot = reinterpret_cast<Slot>(stack[i]);
272 h = (h << 8) | (h >> (8*(sizeof(h)-1)));
273 h += (slot * 31) + (slot * 7) + (slot * 3);
276 count_++;
278 // See if table already has an entry for this trace
279 bool done = false;
280 Bucket* bucket = &hash_[h % kBuckets];
281 for (int a = 0; a < kAssociativity; a++) {
282 Entry* e = &bucket->entry[a];
283 if (e->depth == depth) {
284 bool match = true;
285 for (int i = 0; i < depth; i++) {
286 if (e->stack[i] != reinterpret_cast<Slot>(stack[i])) {
287 match = false;
288 break;
291 if (match) {
292 e->count++;
293 done = true;
294 break;
299 if (!done) {
300 // Evict entry with smallest count
301 Entry* e = &bucket->entry[0];
302 for (int a = 1; a < kAssociativity; a++) {
303 if (bucket->entry[a].count < e->count) {
304 e = &bucket->entry[a];
307 if (e->count > 0) {
308 evictions_++;
309 Evict(*e);
312 // Use the newly evicted entry
313 e->depth = depth;
314 e->count = 1;
315 for (int i = 0; i < depth; i++) {
316 e->stack[i] = reinterpret_cast<Slot>(stack[i]);
321 // This function is safe to call from asynchronous signals (but is not
322 // re-entrant). However, that's not part of its public interface.
323 void ProfileData::FlushEvicted() {
324 if (num_evicted_ > 0) {
325 const char* buf = reinterpret_cast<char*>(evict_);
326 size_t bytes = sizeof(evict_[0]) * num_evicted_;
327 total_bytes_ += bytes;
328 FDWrite(out_, buf, bytes);
330 num_evicted_ = 0;