1 // Copyright (c) 2007, Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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
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.
31 // Author: Sanjay Ghemawat
32 // Chris Demetriou (refactoring)
34 // Collect profiling data.
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()
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
) {
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
));
79 ProfileData::ProfileData()
91 bool ProfileData::Start(const char* fname
,
92 const ProfileData::Options
& options
) {
97 // Open output file and initialize various data structures
98 int fd
= open(fname
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
100 // Can't open outfile for write
104 start_time_
= time(NULL
);
105 fname_
= strdup(fname
);
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
131 ProfileData::~ProfileData() {
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
) {
141 NO_INTR(r
= write(fd
, buf
, len
));
142 RAW_CHECK(r
>= 0, "write failed");
148 static void DumpProcSelfMaps(int fd
) {
149 ProcMapsIterator::Buffer iterbuf
;
150 ProcMapsIterator
it(0, &iterbuf
); // 0 means "current pid"
152 uint64 start
, end
, offset
;
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() {
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
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
190 // Dump "/proc/self/maps" so we get list of mapped shared libraries
191 DumpProcSelfMaps(out_
);
194 fprintf(stderr
, "PROFILE: interrupts/evictions/bytes = %d/%d/%" PRIuS
"\n",
195 count_
, evictions_
, total_bytes_
);
198 void ProfileData::Reset() {
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.
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 {
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';
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() {
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
260 void ProfileData::Add(int depth
, const void* const* stack
) {
265 if (depth
> kMaxStackDepth
) depth
= kMaxStackDepth
;
266 RAW_CHECK(depth
> 0, "ProfileData::Add depth <= 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);
278 // See if table already has an entry for this trace
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
) {
285 for (int i
= 0; i
< depth
; i
++) {
286 if (e
->stack
[i
] != reinterpret_cast<Slot
>(stack
[i
])) {
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
];
312 // Use the newly evicted entry
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
);