1 //
2 // Copyright 2018 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24
25 #include "pxr/base/trace/reporter.h"
26
27 #include "pxr/pxr.h"
28 #include "pxr/base/trace/aggregateTree.h"
29 #include "pxr/base/trace/eventTree.h"
30 #include "pxr/base/trace/reporterDataSourceCollector.h"
31 #include "pxr/base/trace/threads.h"
32
33 #include "pxr/base/tf/mallocTag.h"
34 #include "pxr/base/tf/stringUtils.h"
35 #include "pxr/base/arch/timing.h"
36 #include "pxr/base/js/json.h"
37
38 #include <algorithm>
39 #include <map>
40 #include <ostream>
41 #include <vector>
42
43 using std::ostream;
44 using std::string;
45
46 PXR_NAMESPACE_OPEN_SCOPE
47
48 TF_DEFINE_PUBLIC_TOKENS(TraceReporterTokens, TRACE_REPORTER_TOKENS);
49
50 //
51 // TraceReporter
52 //
53
TraceReporter(const string & label,DataSourcePtr dataSource)54 TraceReporter::TraceReporter(const string& label,
55 DataSourcePtr dataSource) :
56 TraceReporterBase(std::move(dataSource)),
57 _label(label),
58 _groupByFunction(true),
59 _foldRecursiveCalls(false)
60 {
61 _aggregateTree = TraceAggregateTree::New();
62 _eventTree = TraceEventTree::New();
63 }
64
~TraceReporter()65 TraceReporter::~TraceReporter()
66 {
67 }
68
69 static std::string
_IndentString(int indent)70 _IndentString(int indent)
71 {
72 std::string s;
73 s.resize(indent, ' ');
74
75 // Insert '|' characters every 4 spaces.
76 // The magic value of 2 makes it line up with the outer scope.
77 for (int i=2; i<indent; i+=4) {
78 s[i] = '|';
79 }
80 return s;
81 }
82
83 static std::string
_GetKeyName(const TfToken & key)84 _GetKeyName(const TfToken& key)
85 {
86 return key.GetString();
87 }
88
89 static void
_PrintLineTimes(ostream & s,double inclusive,double exclusive,int count,const string & label,int indent,bool recursive_node,int iterationCount)90 _PrintLineTimes(ostream &s, double inclusive, double exclusive,
91 int count, const string& label, int indent,
92 bool recursive_node, int iterationCount)
93 {
94 string inclusiveStr = TfStringPrintf("%9.3f ms ",
95 ArchTicksToSeconds( uint64_t(inclusive * 1e3) / iterationCount ));
96 if (inclusive <= 0)
97 inclusiveStr = string(inclusiveStr.size(), ' ');
98
99 string exclusiveStr = TfStringPrintf("%9.3f ms ",
100 ArchTicksToSeconds( uint64_t(exclusive * 1e3) / iterationCount ));
101 if (exclusive <= 0)
102 exclusiveStr = string(exclusiveStr.size(), ' ');
103
104 string countStr;
105 if (iterationCount == 1)
106 countStr = TfStringPrintf("%7.0f samples ", double(count));
107 else
108 countStr = TfStringPrintf("%10.3f samples ",
109 double(count)/iterationCount);
110
111 if (count <= 0)
112 {
113 // CODE_COVERAGE_OFF -- shouldn't get a count of zero
114 countStr = string(countStr.size(), ' ');
115 // CODE_COVERAGE_ON
116 }
117
118 s << inclusiveStr << exclusiveStr << countStr << " ";
119
120 s << _IndentString(indent);
121
122 // Put a '*' before the label of recursive nodes so that we can easily
123 // identify them.
124 if (recursive_node)
125 s << "*";
126 s << label << "\n";
127 }
128
129 static void
_PrintRecursionMarker(ostream & s,const std::string & label,int indent)130 _PrintRecursionMarker(
131 ostream &s,
132 const std::string &label,
133 int indent)
134 {
135 string inclusiveStr(13, ' ');
136 string exclusiveStr(13, ' ');
137 string countStr(16, ' ');
138
139 // Need -1 here in order to get '|' characters to line up.
140 string indentStr( _IndentString(indent-1) );
141
142 s << inclusiveStr << exclusiveStr << countStr << " " << indentStr << " ";
143 s << "[" << label << "]\n";
144
145 }
146
147 static void
_PrintNodeTimes(ostream & s,TraceAggregateNodeRefPtr node,int indent,int iterationCount)148 _PrintNodeTimes(
149 ostream &s,
150 TraceAggregateNodeRefPtr node,
151 int indent,
152 int iterationCount)
153 {
154 // The root of the tree has id == -1, no useful stats there.
155
156 if (node->GetId().IsValid()) {
157
158 if (node->IsRecursionMarker()) {
159 _PrintRecursionMarker(s, _GetKeyName(node->GetKey()), indent);
160 return;
161 }
162
163 bool r = node->IsRecursionHead();
164 _PrintLineTimes(s, node->GetInclusiveTime(), node->GetExclusiveTime(r),
165 node->GetCount(r), _GetKeyName(node->GetKey()),
166 indent, r, iterationCount);
167 }
168
169 // sort children by inclusive time on output
170 std::vector<TraceAggregateNodeRefPtr> sortedKids;
171 for (const TraceAggregateNodeRefPtr& it : node->GetChildrenRef()) {
172 sortedKids.push_back(it);
173 }
174
175 for (const TraceAggregateNodeRefPtr& it : sortedKids) {
176 _PrintNodeTimes(s, it, indent+2, iterationCount);
177 }
178 }
179
180 void
_PrintTimes(ostream & s)181 TraceReporter::_PrintTimes(ostream &s)
182 {
183 using SortedTimes = std::multimap<TimeStamp, TfToken>;
184
185 SortedTimes sortedTimes;
186 for (const TraceAggregateTree::EventTimes::value_type& it
187 : _aggregateTree->GetEventTimes() ) {
188 sortedTimes.insert(SortedTimes::value_type(it.second, it.first));
189 }
190 for (const SortedTimes::value_type& it : sortedTimes) {
191 s << TfStringPrintf("%9.3f ms ",
192 ArchTicksToSeconds((uint64_t)(it.first * 1e3)))
193 << _GetKeyName(it.second) << "\n";
194 }
195 }
196
197 void
Report(std::ostream & s,int iterationCount)198 TraceReporter::Report(
199 std::ostream &s,
200 int iterationCount)
201 {
202 if (iterationCount < 1) {
203 TF_CODING_ERROR("iterationCount %d is invalid; falling back to 1",
204 iterationCount);
205 iterationCount = 1;
206 }
207
208 UpdateTraceTrees();
209
210 // Fold recursive calls if we need to.
211 if (GetFoldRecursiveCalls()) {
212 _aggregateTree->GetRoot()->MarkRecursiveChildren();
213 }
214
215 if (iterationCount > 1)
216 s << "\nNumber of iterations: " << iterationCount << "\n";
217
218 s << "\nTree view ==============\n";
219 if (iterationCount == 1)
220 s << " inclusive exclusive \n";
221 else {
222 s << " incl./iter excl./iter samples/iter\n";
223 }
224
225 _PrintNodeTimes(s, _aggregateTree->GetRoot(), 0, iterationCount);
226
227 s << "\n";
228 }
229
230 void
ReportTimes(std::ostream & s)231 TraceReporter::ReportTimes(std::ostream &s)
232 {
233 UpdateTraceTrees();
234
235 s << "\nTotal time for each key ==============\n";
236 _PrintTimes(s);
237 s << "\n";
238 }
239
240 void
ReportChromeTracing(std::ostream & s)241 TraceReporter::ReportChromeTracing(std::ostream &s)
242 {
243 UpdateTraceTrees();
244
245 JsWriter w(s);
246 _eventTree->WriteChromeTraceObject(w);
247 }
248
249
250 void
_RebuildEventAndAggregateTrees()251 TraceReporter::_RebuildEventAndAggregateTrees()
252 {
253 // Get the latest from the collector and process the events.
254 _Update();
255
256 // If MallocTags were enabled for the capture of this trace, add a dummy
257 // warning node as an indicator that the trace may have been slowed down
258 // by the memory tagging, unless there was nothing reported anyway.
259 // XXX: add "WARNING" token that Spy can use.
260 TraceAggregateNodePtr root = _aggregateTree->GetRoot();
261 if (root && !root->GetChildrenRef().empty() &&
262 TfMallocTag::IsInitialized()) {
263 root->Append(TraceAggregateNode::Id(),
264 TfToken(
265 TraceReporterTokens->warningString.GetString() +
266 " MallocTags enabled"),
267 0,
268 1 /* count */,
269 1 /* exclusive count */);
270 }
271 }
272
273 void
UpdateTraceTrees()274 TraceReporter::UpdateTraceTrees()
275 {
276 _RebuildEventAndAggregateTrees();
277 }
278
279 void
ClearTree()280 TraceReporter::ClearTree()
281 {
282 _aggregateTree->Clear();
283 _eventTree = TraceEventTree::New();
284 _Clear();
285 }
286
287 TraceAggregateNodePtr
GetAggregateTreeRoot()288 TraceReporter::GetAggregateTreeRoot()
289 {
290 return _aggregateTree->GetRoot();
291 }
292
293 TraceEventNodeRefPtr
GetEventRoot()294 TraceReporter::GetEventRoot()
295 {
296 return _eventTree->GetRoot();
297 }
298
299 TraceEventTreeRefPtr
GetEventTree()300 TraceReporter::GetEventTree()
301 {
302 return _eventTree;
303 }
304
305
306 const TraceReporter::CounterMap &
GetCounters() const307 TraceReporter::GetCounters() const
308 {
309 return _aggregateTree->GetCounters();
310 }
311
312 int
GetCounterIndex(const TfToken & key) const313 TraceReporter::GetCounterIndex(const TfToken &key) const
314 {
315 return _aggregateTree->GetCounterIndex(key);
316 }
317
318 bool
AddCounter(const TfToken & key,int index,double totalValue)319 TraceReporter::AddCounter(const TfToken &key, int index, double totalValue)
320 {
321 return _aggregateTree->AddCounter(key, index, totalValue);
322 }
323
324 void
SetGroupByFunction(bool groupByFunction)325 TraceReporter::SetGroupByFunction(bool groupByFunction)
326 {
327 _groupByFunction = groupByFunction;
328 }
329
330 bool
GetGroupByFunction() const331 TraceReporter::GetGroupByFunction() const
332 {
333 return _groupByFunction;
334 }
335
336 void
SetFoldRecursiveCalls(bool foldRecursiveCalls)337 TraceReporter::SetFoldRecursiveCalls(bool foldRecursiveCalls)
338 {
339 _foldRecursiveCalls = foldRecursiveCalls;
340 }
341
342 bool
GetFoldRecursiveCalls() const343 TraceReporter::GetFoldRecursiveCalls() const
344 {
345 return _foldRecursiveCalls;
346 }
347
348 /* static */
349 TraceAggregateNode::Id
CreateValidEventId()350 TraceReporter::CreateValidEventId()
351 {
352 return TraceAggregateNode::Id(TraceGetThreadId());
353 }
354
355 void
_ProcessCollection(const TraceReporterBase::CollectionPtr & collection)356 TraceReporter::_ProcessCollection(
357 const TraceReporterBase::CollectionPtr& collection)
358 {
359 if (collection) {
360
361 // We just always build the single (additional) event tree for the
362 // (additional) new collection given and pass it on to the aggregate
363 // tree. Note that the call to Add() merges in the newGraph to
364 // _eventTree which thus represents the merged sum of all collections
365 // seen here whereas newGraph is just the graph for the new collection.
366
367 TraceEventTreeRefPtr newGraph = _eventTree->Add(*collection);
368 _aggregateTree->Append(newGraph, *collection);
369 }
370 }
371
372 TraceReporterPtr
GetGlobalReporter()373 TraceReporter::GetGlobalReporter()
374 {
375 // Note that, like TfSingleton, the global reporter instance is not freed
376 // at shutdown.
377 static const TraceReporterPtr globalReporter(
378 new TraceReporter(
379 "Trace global reporter",
380 TraceReporterDataSourceCollector::New()));
381 return globalReporter;
382 }
383
384 PXR_NAMESPACE_CLOSE_SCOPE
385