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