• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
17 //
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.
29 //
30 // ---
31 // Author: Sanjay Ghemawat
32 //         Chris Demetriou (refactoring)
33 //
34 // Collect profiling data.
35 
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>
47 
48 #include "profiledata.h"
49 
50 #include "base/logging.h"
51 #include "base/sysinfo.h"
52 
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;
58 
Options()59 ProfileData::Options::Options()
60     : frequency_(1) {
61 }
62 
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.
Evict(const Entry & entry)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);
72   }
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;
77 }
78 
ProfileData()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) {
89 }
90 
Start(const char * fname,const ProfileData::Options & options)91 bool ProfileData::Start(const char* fname,
92                         const ProfileData::Options& options) {
93   if (enabled()) {
94     return false;
95   }
96 
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;
102   }
103 
104   start_time_ = time(NULL);
105   fname_ = strdup(fname);
106 
107   // Reset counters
108   num_evicted_ = 0;
109   count_       = 0;
110   evictions_   = 0;
111   total_bytes_ = 0;
112 
113   hash_ = new Bucket[kBuckets];
114   evict_ = new Slot[kBufferLength];
115   memset(hash_, 0, sizeof(hash_[0]) * kBuckets);
116 
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
125 
126   out_ = fd;
127 
128   return true;
129 }
130 
~ProfileData()131 ProfileData::~ProfileData() {
132   Stop();
133 }
134 
135 // Dump /proc/maps data to fd.  Copied from heap-profile-table.cc.
136 #define NO_INTR(fn)  do {} while ((fn) < 0 && errno == EINTR)
137 
FDWrite(int fd,const char * buf,size_t len)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;
145   }
146 }
147 
DumpProcSelfMaps(int fd)148 static void DumpProcSelfMaps(int fd) {
149   ProcMapsIterator::Buffer iterbuf;
150   ProcMapsIterator it(0, &iterbuf);   // 0 means "current pid"
151 
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,
159                                 0);
160     FDWrite(fd, linebuf.buf_, written);
161   }
162 }
163 
Stop()164 void ProfileData::Stop() {
165   if (!enabled()) {
166     return;
167   }
168 
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]);
175       }
176     }
177   }
178 
179   if (num_evicted_ + 3 > kBufferLength) {
180     // Ensure there is enough room for end of data marker
181     FlushEvicted();
182   }
183 
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();
189 
190   // Dump "/proc/self/maps" so we get list of mapped shared libraries
191   DumpProcSelfMaps(out_);
192 
193   Reset();
194   fprintf(stderr, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS "\n",
195           count_, evictions_, total_bytes_);
196 }
197 
Reset()198 void ProfileData::Reset() {
199   if (!enabled()) {
200     return;
201   }
202 
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;
215 
216   out_ = -1;
217 }
218 
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.
GetCurrentState(State * state) const221 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';
234   }
235 }
236 
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.
FlushTable()239 void ProfileData::FlushTable() {
240   if (!enabled()) {
241     return;
242   }
243 
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;
252       }
253     }
254   }
255 
256   // Write out all pending data
257   FlushEvicted();
258 }
259 
Add(int depth,const void * const * stack)260 void ProfileData::Add(int depth, const void* const* stack) {
261   if (!enabled()) {
262     return;
263   }
264 
265   if (depth > kMaxStackDepth) depth = kMaxStackDepth;
266   RAW_CHECK(depth > 0, "ProfileData::Add depth <= 0");
267 
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);
274   }
275 
276   count_++;
277 
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;
289         }
290       }
291       if (match) {
292         e->count++;
293         done = true;
294         break;
295       }
296     }
297   }
298 
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];
305       }
306     }
307     if (e->count > 0) {
308       evictions_++;
309       Evict(*e);
310     }
311 
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]);
317     }
318   }
319 }
320 
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.
FlushEvicted()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);
329   }
330   num_evicted_ = 0;
331 }
332