Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
calltracemanager.cc
Go to the documentation of this file.
1 
19 
20 #include <fstream> // NOLINT
21 
24 #include "ion/base/serialize.h"
25 #include "ion/base/stringutils.h"
26 #include "ion/port/threadutils.h"
30 #include "third_party/jsoncpp/include/json/json.h"
31 
32 namespace ion {
33 namespace profile {
34 
35 namespace {
36 
39 struct ChunkInfo {
40  uint32 id;
41  uint32 type;
42  uint32 length;
43  uint32 start_time;
44  uint32 end_time;
45  uint32 part_count;
46 };
47 
50 struct PartInfo {
51  uint32 type;
52  uint32 offset;
53  uint32 length;
54 };
55 
57 static uint32 UpToNearestFour(uint32 n) {
58  if (n % 4 == 0) {
59  return n;
60  }
61  return n + (4 - (n % 4));
62 }
63 
71 class Part {
72  public:
73  virtual ~Part() {}
74 
75  virtual uint32 GetRawSizeInBytes() const = 0;
76 
77  virtual void AppendToString(std::string* output) const = 0;
78 
82  uint32 GetAlignedSizeInBytes() const {
83  return UpToNearestFour(GetRawSizeInBytes());
84  }
85 };
86 
102 struct Chunk {
103  void AddPart(uint32 type, Part* part) {
104  DCHECK(part);
105 
106  PartInfo info;
107  info.type = type;
108  info.length = part->GetRawSizeInBytes();
109  info.offset = 0;
110  for (size_t i = 0; i < parts.size(); i++) {
111  info.offset += parts[i]->GetAlignedSizeInBytes();
112  }
113  part_headers.push_back(info);
114  parts.push_back(part);
115  }
116 
117  void AppendToString(uint32 id, uint32 type, std::string* output) {
118  DCHECK(parts.size() == part_headers.size());
119 
120  ChunkInfo info;
121  info.part_count = static_cast<uint32>(parts.size());
122  info.id = id;
123  info.type = type;
124  info.length = static_cast<uint32>(sizeof(ChunkInfo)) +
125  static_cast<uint32>(sizeof(PartInfo)) * info.part_count;
126  for (size_t i = 0; i < parts.size(); i++) {
127  info.length += parts[i]->GetAlignedSizeInBytes();
128  }
129  info.start_time = -1;
130  info.end_time = -1;
131 
132  base::AppendBytes(output, info);
133  for (size_t i = 0; i < part_headers.size(); i++) {
134  base::AppendBytes(output, part_headers[i]);
135  }
136  for (size_t i = 0; i < parts.size(); i++) {
137  parts[i]->AppendToString(output);
138  }
139  }
140 
141  std::vector<PartInfo> part_headers;
142  std::vector<Part*> parts;
143 };
144 
145 class StringTable : public Part {
146  public:
147  StringTable()
148  : include_null_(true) {}
149 
150  explicit StringTable(bool include_null)
151  : include_null_(include_null) {}
152 
153  void AddString(const std::string& str) {
154  const std::vector<std::string> split = base::SplitString(str, "\n");
155  for (size_t i = 0; i < split.size(); ++i) {
156  table_.push_back(split[i]);
157  }
158  }
159 
160  uint32 GetRawSizeInBytes() const override {
161  size_t raw_size_in_bytes = 0;
162  for (size_t i = 0; i < table_.size(); ++i) {
163  const size_t string_length = table_[i].length();
164  if (include_null_) {
165  raw_size_in_bytes += string_length + 1;
166  } else {
167  raw_size_in_bytes += string_length;
168  }
169  }
170  return static_cast<uint32>(raw_size_in_bytes);
171  }
172 
173  uint32 GetTableSize() const { return static_cast<uint32>(table_.size()); }
174 
175  void AppendToString(std::string* output) const override {
176  for (size_t i = 0; i < table_.size(); ++i) {
177  output->append(table_[i]);
178  if (include_null_) {
180  char ch = 0;
181  base::AppendBytes(output, ch);
182  }
183  }
184 
187  uint32 extra = GetAlignedSizeInBytes() - GetRawSizeInBytes();
188  for (uint32 i = 0; i < extra; ++i) {
189  char ch = 0;
190  base::AppendBytes(output, ch);
191  }
192  }
193 
194  std::vector<std::string>* GetMutableTable() { return &table_; }
195 
196  private:
197  std::vector<std::string> table_;
198  const bool include_null_;
199 };
200 
201 struct EventBuffer : public Part {
202  public:
203  uint32 GetRawSizeInBytes() const override {
204  return static_cast<uint32>(buffer.length());
205  }
206 
207  void AppendToString(std::string* output) const override {
208  output->append(buffer);
209  }
210 
211  std::string buffer;
212 };
213 
214 } // namespace
215 
217  : recorder_(recorder) {
218  DCHECK(recorder_ && id);
219  recorder_->EnterScope(id);
220 }
221 
223  DCHECK(recorder_);
224  recorder_->LeaveScope();
225 }
226 
228  : recorder_(recorder) {
229  DCHECK(recorder_);
230  recorder_->EnterFrame(id);
231 }
232 
234  DCHECK(recorder_);
235  recorder_->LeaveFrame();
236 }
237 
239  : trace_recorder_(GetAllocator()),
240  named_trace_recorders_(GetAllocator()),
241  recorder_list_(GetAllocator()),
242  buffer_size_(0),
243  scope_event_map_(GetAllocator()),
244  reverse_scope_event_map_(GetAllocator()) {
245 }
246 
248  : trace_recorder_(GetAllocator()),
249  named_trace_recorders_(GetAllocator()),
250  recorder_list_(GetAllocator()),
251  buffer_size_(buffer_size),
252  scope_event_map_(GetAllocator()),
253  reverse_scope_event_map_(GetAllocator()) {
254 }
255 
257  base::LockGuard lock(&mutex_);
258 
259  if (!timeline_metrics_.empty()) {
261  ion::analytics::OutputBenchmarkPretty("Timeline Metrics", false, benchmark,
262  std::cout);
263  }
264 
265  for (size_t i = 0; i < recorder_list_.size(); ++i) {
266  delete recorder_list_[i];
267  recorder_list_[i] = NULL;
268  }
269 }
270 
272  void* ptr = port::GetThreadLocalStorage(trace_recorder_.GetKey());
273  if (ptr) {
274  return *static_cast<TraceRecorder**>(ptr);
275  } else {
276  TraceRecorder** recorder = trace_recorder_.Get();
277  *recorder = AllocateTraceRecorder();
278  return *recorder;
279  }
280 }
281 
285  NamedTraceRecorderArray* recorders = named_trace_recorders_.Get();
286  TraceRecorder* recorder = recorders->recorders[name];
287  if (recorder) {
288  return recorder;
289  }
290  recorder = AllocateTraceRecorder();
291  switch (name) {
292  case kRecorderGpu:
293  recorder->SetThreadName("GPU");
294  break;
295  case kRecorderVSync:
296  recorder->SetThreadName("VSync");
297  break;
298  default:
299  LOG(WARNING) << "Unknown name(" << name << ") for named TraceRecorder.";
300  }
301  recorders->recorders[name] = recorder;
302  return recorder;
303 }
304 
305 TraceRecorder* CallTraceManager::AllocateTraceRecorder() {
306  TraceRecorder* recorder = NULL;
307  if (buffer_size_ == 0) {
308  recorder = new (GetAllocator()) TraceRecorder(this);
309  } else {
310  recorder = new (GetAllocator()) TraceRecorder(this, buffer_size_);
311  }
312  {
313  base::LockGuard lock(&mutex_);
314  recorder_list_.push_back(recorder);
315  }
316  return recorder;
317 }
318 
319 int CallTraceManager::GetScopeEnterEvent(const char* string_id) {
320  base::LockGuard lock(&mutex_);
321 
322  ScopeEventMap::const_iterator it = scope_event_map_.find(string_id);
323  if (it != scope_event_map_.end()) {
324  return it->second;
325  }
326 
327  const int new_event_id =
328  static_cast<int>(kCustomScopeEvent + GetNumScopeEvents());
329  scope_event_map_.insert(
330  std::make_pair(static_cast<const void*>(string_id), new_event_id));
331  reverse_scope_event_map_.insert(std::make_pair(new_event_id, string_id));
332  return new_event_id;
333 }
334 
335 const char* CallTraceManager::GetScopeEnterEventName(uint32 event_id) const {
336  return reverse_scope_event_map_.at(event_id);
337 }
338 
343  static const int kBuiltinEventArgNum[] = {
344  0, 5, 0, 4, 1, 1, 1, 0, 0, 2, 2, 2, 3, 1, 1, 1, 1, 1};
345  if (event_id >= kCustomScopeEvent) {
346  event_id = 0;
347  }
348  return kBuiltinEventArgNum[event_id];
349 }
350 
352  uint32 event_id, int arg_index) {
353  CHECK(event_id < kCustomScopeEvent);
354 
357  static const int kOffsetTable[] = {
358  0, 1, 6, 7, 11, 12, 13, 14, 15, 16, 18, 20, 22, 25, 26, 27, 28, 29};
359 
363  static const EventArgType kBuiltinEventArgTypes[] = {
364  kArgNone, // The zeroth event is reserved and has no arguments.
366  kArgNone,
368  kArgNumeric,
369  kArgNumeric,
370  kArgString,
371  kArgNone,
372  kArgNone,
377  kArgNumeric,
378  kArgNumeric,
379  kArgNumeric,
380  kArgString,
381  kArgNumeric};
382 
383  return kBuiltinEventArgTypes[kOffsetTable[event_id] + arg_index];
384 }
385 
387  std::string output;
388 
389  static const uint32 magic_number = 0xdeadbeef;
390  static const uint32 wtf_version = 0xe8214400;
391  static const uint32 format_version = 10;
392 
393  base::AppendBytes(&output, magic_number);
394  base::AppendBytes(&output, wtf_version);
395  base::AppendBytes(&output, format_version);
396 
398  Json::Value flags(Json::arrayValue);
399  flags.append("has_high_resolution_times");
400 
403  Json::Value icon(Json::objectValue);
404  icon["uri"] = "https://maps.gstatic.com/favicon3.ico";
405 
408  Json::Value agent(Json::objectValue);
409  agent["device"] = "Ion";
410  agent["platform"] = "SomePlatform";
411  agent["platformVersion"] = "";
412  agent["type"] = "";
413  agent["value"] = "";
414 
415  Json::Value context(Json::objectValue);
416  context["args"] = Json::Value(Json::arrayValue);
417  context["contextType"] = "script";
418  context["icon"] = icon;
419  context["taskId"] = "";
420  context["title"] = "Ion";
422  context["userAgent"] = agent;
423 
424  Json::Value json;
425  json["type"] = "file_header";
426  json["flags"] = flags;
428  json["timebase"] = 1412611454780.701;
429  json["contextInfo"] = context;
430 
431  Json::FastWriter json_writer;
432  std::string json_string = json_writer.write(json);
433  StringTable file_header_table(false);
434  file_header_table.AddString(json_string);
435 
436  Chunk file_header;
437  file_header.AddPart(0x10000, &file_header_table);
438  file_header.AppendToString(2, 0x1, &output);
439 
440 
441  StringTable def_table;
442  def_table.AddString(
443  "wtf.event#define\n"
444  "uint16 wireId, uint16 eventClass, uint32 flags, ascii name, ascii args\n"
445  "wtf.trace#discontinuity\n"
446  "wtf.zone#create\n"
447  "uint16 zoneId, ascii name, ascii type, ascii location\n"
448  "wtf.zone#delete\n"
449  "uint16 zoneId\n"
450  "wtf.zone#set\n"
451  "uint16 zoneId\n"
452  "wtf.scope#enter\n"
453  "ascii name\n"
454  "wtf.scope#enterTracing\n"
455  "wtf.scope#leave\n"
456  "wtf.scope#appendData\n"
457  "ascii name, any value\n"
458  "wtf.trace#mark\n"
459  "ascii name, any value\n"
460  "wtf.trace#timeStamp\n"
461  "ascii name, any value\n"
462  "wtf.timeRange#begin\n"
463  "uint32 id, ascii name, any value\n"
464  "wtf.timeRange#end\n"
465  "uint32 id\n"
466  "wtf.timing#frameStart\n"
467  "uint32 number\n"
468  "wtf.timing#frameEnd\n"
469  "uint32 number\n"
470  "wtf.scope#appendData_url_utf8\n"
471  "utf8 url\n"
472  "wtf.scope#appendData_readyState_int32\n"
473  "int32 readyState");
474 
476  const uint32 event_string_offset = def_table.GetTableSize();
477  for (ScopeEventMap::const_iterator it = scope_event_map_.begin();
478  it != scope_event_map_.end(); ++it) {
479  def_table.AddString(reinterpret_cast<const char*>(it->first));
480  }
481 
502 
503  EventBuffer def_events;
504  {
505  std::string* event_buffer = &def_events.buffer;
506  uint32 builtin[] = {
507  1, 0, 1, 0, 40, 0, 1,
508  1, 0, 2, 0, 32, 2, 0xffffffff,
509  1, 0, 3, 0, 40, 3, 4,
510  1, 0, 4, 0, 40, 5, 6,
511  1, 0, 5, 0, 40, 7, 8,
512  1, 0, 6, 1, 32, 9, 10,
513  1, 0, 7, 1, 44, 11, 0xffffffff,
514  1, 0, 8, 0, 40, 12, 0xffffffff,
515  1, 0, 9, 0, 56, 13, 14,
516  1, 0, 10, 0, 40, 15, 16,
517  1, 0, 11, 0, 32, 17, 18,
518  1, 0, 12, 0, 40, 19, 20,
519  1, 0, 13, 0, 40, 21, 22,
520  1, 0, 14, 0, 8, 23, 24,
521  1, 0, 15, 0, 8, 25, 26,
522  1, 0, 16, 0, 24, 27, 28,
523  1, 0, 17, 0, 24, 29, 30
524  };
525  base::AppendBytes(event_buffer, builtin);
526 
528  {
529  int event_counter = 0;
530  uint32 temp;
531  for (ScopeEventMap::const_iterator it = scope_event_map_.begin();
532  it != scope_event_map_.end(); ++it) {
533  temp = kDefineEvent;
534  base::AppendBytes(event_buffer, temp); // wtf.event#define
535  temp = 0;
536  base::AppendBytes(event_buffer, temp); // timestamp
537  temp = it->second;
538  base::AppendBytes(event_buffer, temp); // wireId
539  temp = 1;
540  base::AppendBytes(event_buffer, temp); // eventClass (scope)
541  temp = 0;
542  base::AppendBytes(event_buffer, temp); // flags (unused)
543  temp = event_string_offset + event_counter;
544  base::AppendBytes(event_buffer, temp); // name
545  temp = -1;
546  base::AppendBytes(event_buffer, temp); // args (none)
547  event_counter++;
548  }
549  }
550  }
551 
552  Chunk events_defined;
553  events_defined.AddPart(0x30000, &def_table);
554  events_defined.AddPart(0x20002, &def_events);
555  events_defined.AppendToString(3, 0x2, &output);
556 
557 
558  const int num_trace_threads = static_cast<int>(recorder_list_.size());
559  StringTable table;
560  table.AddString("script");
561  table.AddString("Some_Location");
562  for (int chunk_i = 0; chunk_i < num_trace_threads; ++chunk_i) {
563  const int zone_id = chunk_i + 1;
564  std::string location_string = "Thread_" + base::ValueToString(zone_id);
565  table.AddString(location_string);
566  }
567 
568  EventBuffer events;
569  {
570  std::string* event_buffer = &events.buffer;
571  for (int chunk_i = 0; chunk_i < num_trace_threads; ++chunk_i) {
572  const int zone_id = chunk_i + 1;
573  uint32 temp;
574 
576  temp = kCreateZoneEvent;
577  base::AppendBytes(event_buffer, temp);
578  temp = 0;
579  base::AppendBytes(event_buffer, temp); // timestamp
580  temp = zone_id;
581  base::AppendBytes(event_buffer, temp); // Zone id
582  temp = 2 + chunk_i;
583  base::AppendBytes(event_buffer, temp); // Zone name
584  temp = 0;
585  base::AppendBytes(event_buffer, temp); // Zone type
586  temp = 1;
587  base::AppendBytes(event_buffer, temp); // Zone location
588  }
589 
590  for (int chunk_i = 0; chunk_i < num_trace_threads; ++chunk_i) {
591  TraceRecorder* rec = recorder_list_[chunk_i];
592  const int zone_id = chunk_i + 1;
593  uint32 temp;
594 
596  temp = kSetZoneEvent;
597  base::AppendBytes(event_buffer, temp);
598  temp = 0;
599  base::AppendBytes(event_buffer, temp); // timestamp
600  temp = zone_id;
601  base::AppendBytes(event_buffer, temp); // Zone id
602 
604  rec->DumpTrace(event_buffer, table.GetTableSize());
605 
607  rec->DumpStrings(table.GetMutableTable());
608  }
609  }
610 
611  Chunk trace;
612  trace.AddPart(0x30000, &table);
613  trace.AddPart(0x20002, &events);
614  trace.AppendToString(1, 0x2, &output);
615 
616  return output;
617 }
618 
619 void CallTraceManager::WriteFile(const std::string& filename) const {
620  if (!filename.empty()) {
621  LOG(INFO) << "Writing current WTF traces to: " << filename;
622  std::ofstream filestream(
623  filename.c_str(), std::ios::out | std::ios::binary);
624  if (filestream.good()) {
625  filestream << SnapshotCallTraces();
626  filestream.close();
627  } else {
628  LOG(WARNING) << "Failed to open " << filename
629  << " for writing WTF traces.";
630  }
631  }
632 }
633 
635  std::unique_ptr<TimelineNode> root(new TimelineNode("root"));
636  for (const auto recorder : recorder_list_) {
637  std::unique_ptr<TimelineThread> thread(
638  new TimelineThread(recorder->GetThreadName(), recorder->GetThreadId()));
639  recorder->AddTraceToTimelineNode(thread.get());
640  root->AddChild(std::move(thread));
641  }
642 
643  return Timeline(std::move(root));
644 }
645 
647  analytics::Benchmark benchmark;
648  Timeline timeline = BuildTimeline();
649  for (const auto& metric : timeline_metrics_)
650  metric->Run(timeline, &benchmark);
651  return benchmark;
652 }
653 
654 } // namespace profile
655 } // namespace ion
std::string buffer
uint32 type
The event for defining new WTF events, both custom and built-in events.
const std::string & str
#define CHECK(expr)
Definition: logging.h:323
static int GetNumArgsForEvent(uint32 event_id)
Gets the number of arguments for a particular event.
std::vector< Part * > parts
const bool include_null_
Class for recording frame events.
Definition: tracerecorder.h:42
void SetThreadName(const std::string &name)
Sets a name for the thread that this recorder is tracing.
Copyright 2016 Google Inc.
#define DCHECK(expr)
Definition: logging.h:331
GenericLockGuard< port::Mutex > LockGuard
Convenient typedefs for ion::port::Mutex.
Definition: lockguards.h:192
void AddChild(std::unique_ptr< TimelineNode > child)
Add a node to the children. It becomes the last child.
Definition: timelinenode.h:71
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Definition: logging.h:216
static EventArgType GetArgType(uint32 event_id, int arg_index)
Gets the argument types for a particular event.
Table table
Definition: printer.cc:333
uint32 offset
std::vector< std::string > ION_API SplitString(const std::string &str, const std::string &delimiters)
Splits a string into a vector of substrings, given a set of delimiter characters (expressed as a stri...
Definition: stringutils.cc:187
Copyright 2016 Google Inc.
Definition: timeline.h:47
uint32 length
uint32 end_time
uint32 id
void AddTraceToTimelineNode(TimelineNode *root) const
Adds all events in the trace as a sub-tree under the passed in root node.
void WriteFile(const std::string &filename) const
Writes the current WTF trace to a file, which usually ends in the extension ".wtf-trace".
void LeaveScope()
Leaves the current (most recent) scope.
A LockGuard locks a mutex when created, and unlocks it when destroyed.
Definition: lockguards.h:90
std::string ValueToString(const T &val)
ValueToString.
Definition: serialize.h:209
NamedTraceRecorderType
Defines name trace recorders.
void AppendBytes(std::string *s, const T &value)
Convenience method to append bytes to string.
Definition: stringutils.h:253
void DumpStrings(std::vector< std::string > *table) const
Dumps the strings one by one to a string table.
std::string name
Definition: printer.cc:324
EventArgType
Defines argument types for WTF events.
void LeaveFrame()
Records a frame exit event for the current frame index.
Copyright 2016 Google Inc.
Definition: timelinenode.h:28
std::vector< std::string > table_
void OutputBenchmarkPretty(const std::string &id_string, bool print_descriptions, const Benchmark &benchmark, std::ostream &out)
Outputs benchmark results in pretty format.
void EnterScope(int event_id)
Queries and records the event corresponding to the provided event_id.
void DumpTrace(std::string *output, uint32 string_index_offset) const
Appends a binary dump of the trace to the output string.
void EnterFrame(uint32 frame_number)
Records a frame enter event with a specified frame index.
std::string SnapshotCallTraces() const
Returns a snapshot of traces to a string in binary .wtf-trace format.
uint32 part_count
TraceRecorder * GetNamedTraceRecorder(NamedTraceRecorderType name)
Gets the TraceRecorder instance specific to the current thread of the given name. ...
analytics::Benchmark RunTimelineMetrics() const
Runs all registered metrics on the current timeline and returns a benchmark object containing the col...
ScopedFrameTracer(TraceRecorder *recorder, uint32 id)
T * Get()
Returns a T instance for the current thread, creating it first if necessary.
std::vector< PartInfo > part_headers
TraceRecorder * GetTraceRecorder()
Gets the TraceRecorder instance specific to the current thread.
const ion::port::ThreadId & GetThreadId() const
Returns the ID of the thread that this recorder is tracing.
size_t GetNumScopeEvents() const
Returns the number of unique custom scope events recorded.
The starting offset for scope event ids, purposefully set high to make room for built-in WTF events...
std::string GetThreadName() const
Returns the name for the thread that this recorder is tracing.
int GetScopeEnterEvent(const char *string_id)
Queries the event id of a scope enter event, based on string_id.
The Benchmark class provides types and utilities to make it easier to create performance benchmarks...
Definition: benchmark.h:34
ScopedTracer(TraceRecorder *recorder, int id)
const AllocatorPtr & GetAllocator() const
Returns the Allocator that was used for the instance.
Definition: allocatable.h:68
void * GetThreadLocalStorage(ThreadLocalStorageKey key)
Returns the pointer to the thread-local storage area indicated by key.
Definition: threadutils.cc:382
uint32 start_time
Timeline BuildTimeline() const
Convert the current WTF trace into a timeline.
const char * GetScopeEnterEventName(uint32 event_id) const
Returns the name of the scope enter event with the given id.