profiledata.cc 9.2 KB

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