Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(357)

Side by Side Diff: base/test/trace_event_analyzer.h

Issue 7981004: add classes trace_analyzer::Query and TraceAnalyzer to make it easy to search through trace data (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comments updated Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Use trace_analyzer::Query and trace_analyzer::TraceAnalyzer to search for
6 // specific trace events that were generated by the trace_event.h API.
7 //
8 // Basic procedure:
9 // - Get trace events JSON string from base::debug::TraceLog.
10 // - Create TraceAnalyzer with JSON string.
11 // - Call TraceAnalyzer::AssociateBeginEndEvents (optional).
12 // - Call TraceAnalyzer::AssociateEvents (zero or more times).
13 // - Call TraceAnalyzer::FindEvents with queries to find specific events.
14 //
15 // A Query is a boolean expression tree that evaluates to true or false for a
16 // given trace event. Queries can be combined into a tree using boolean,
17 // arithmetic and comparison operators that refer to data of an individual trace
18 // event.
19 //
20 // The events are returned as trace_analyzer::TraceEvent objects.
21 // TraceEvent contains a single trace event's data, as well as a pointer to
22 // a related trace event. The related trace event is typically the matching end
23 // of a begin event or the matching begin of an end event.
24 //
25 // The following examples use this basic setup code to construct TraceAnalyzer
26 // with the json trace string retrieved from TraceLog and construct an event
27 // vector for retrieving events:
28 //
29 // TraceAnalyzer analyzer(json_events);
30 // TraceEventVector events;
31 //
32 // During construction, TraceAnalyzer::SetDefaultAssociations is called to
33 // associate all matching begin/end pairs similar to how they are shown in
34 // about:tracing.
35 //
36 // EXAMPLE 1: Find events named "my_event".
37 //
38 // analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
39 //
40 // EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
41 //
42 // Query q = (Query(EVENT_NAME) == Query::String("my_event") &&
43 // Query(EVENT_PHASE) == Query::Phase(TRACE_EVENT_PHASE_BEGIN) &&
44 // Query(EVENT_DURATION) > Query::Double(1000000.0));
45 // analyzer.FindEvents(q, &events);
46 //
47 // EXAMPLE 3: Associating event pairs across threads.
48 //
49 // If the test needs to analyze something that starts and ends on different
50 // threads, the test needs to use INSTANT events. The typical procedure is to
51 // specify the same unique ID as a TRACE_EVENT argument on both the start and
52 // finish INSTANT events. Then use the following procedure to associate those
53 // events.
54 //
55 // Step 1: instrument code with custom begin/end trace events.
56 // [Thread 1 tracing code]
57 // TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
58 // [Thread 2 tracing code]
59 // TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
60 //
61 // Step 2: associate these custom begin/end pairs.
62 // Query begin(Query(EVENT_NAME) == Query::String("timing1_begin"));
63 // Query end(Query(EVENT_NAME) == Query::String("timing1_end"));
64 // Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
65 // analyzer.AssociateEvents(begin, end, match);
66 //
67 // Step 3: search for "timing1_begin" events with existing other event.
68 // Query q = (Query(EVENT_NAME) == Query::String("timing1_begin") &&
69 // Query(EVENT_HAS_OTHER));
70 // analyzer.FindEvents(q, &events);
71 //
72 // Step 4: analyze events, such as checking durations.
73 // for (size_t i = 0; i < events.size(); ++i) {
74 // double duration;
75 // EXPECT_TRUE(events[i].GetAbsTimeToOtherEvent(&duration));
76 // EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
77 // }
78
79
80 #ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
81 #define BASE_TEST_TRACE_EVENT_ANALYZER_H_
82 #pragma once
83
84 #include <map>
85
86 #include "base/debug/trace_event.h"
87 #include "base/memory/ref_counted_memory.h"
88 #include "base/memory/scoped_ptr.h"
89
90 namespace base {
91 class Value;
92 }
93
94 namespace trace_analyzer {
95 class QueryNode;
96
97 // trace_analyzer::TraceEvent is a more convenient form of the
98 // base::debug::TraceEvent class to make tracing-based tests easier to write.
99 struct BASE_EXPORT TraceEvent {
100 // PidTid contains a Process ID and Thread ID.
101 struct PidTid {
jar (doing other things) 2011/10/26 19:35:20 nit: Avoid Abreviations. Suggest: ProcessIdThrea
jar (doing other things) 2011/10/28 20:47:53 nit: You didn't change this.... did you feel this
jbates 2011/10/28 21:54:28 Oversight, oops! Done.
102 PidTid() : pid(0), tid(0) {}
103 PidTid(int pid, int tid) : pid(pid), tid(tid) {}
104 bool operator< (PidTid rhs) const {
105 if (pid != rhs.pid)
106 return pid < rhs.pid;
107 return tid < rhs.tid;
108 }
109 int pid;
110 int tid;
111 };
112
113 TraceEvent();
114 ~TraceEvent();
115
116 bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT;
117
118 bool operator< (const TraceEvent& rhs) const {
119 return timestamp < rhs.timestamp;
120 }
121
122 bool has_other_event() const { return other_event; }
123
124 // Returns absolute duration in microseconds between this event and other
125 // event. Returns false if has_other_event() is false.
126 bool GetAbsTimeToOtherEvent(double* duration) const;
127
128 // Return the argument value if it exists and it is a string.
129 bool GetArgAsString(const std::string& name, std::string* arg) const;
130 // Return the argument value if it exists and it is a number.
131 bool GetArgAsNumber(const std::string& name, double* arg) const;
132
133 // Process ID and Thread ID.
134 PidTid pid_tid;
135
136 // Time since epoch in microseconds.
137 // Stored as double to match its JSON representation.
138 double timestamp;
jar (doing other things) 2011/10/26 19:35:20 I was surprised to see double here (and throughout
jbates 2011/10/26 21:51:54 This starts as TimeTicks in the original trace dat
139
140 base::debug::TraceEventPhase phase;
141
142 std::string category;
143
144 std::string name;
145
146 // All numbers and bool values from TraceEvent args are cast to double.
147 // bool becomes 1.0 (true) or 0.0 (false).
148 std::map<std::string, double> arg_numbers;
jar (doing other things) 2011/10/26 19:35:20 If this is indeed a TraceEvent (as its name sugges
jbates 2011/10/26 21:51:54 Sorry if it wasn't clear form the description. Thi
149
150 std::map<std::string, std::string> arg_strings;
151
152 // The other event associated with this event (or NULL).
153 const TraceEvent* other_event;
154 };
155
156 // Pass these values to Query to compare with the corresponding member of a
157 // TraceEvent.
158 enum TraceEventMember {
159 EVENT_INVALID,
160 // Use these to access the event members:
161 EVENT_PID,
162 EVENT_TID,
163 // Return the timestamp of the event in microseconds since epoch.
164 EVENT_TIME,
165 // Return the absolute time between event and other event in microseconds.
166 // Only works for events with associated BEGIN/END: Query(EVENT_HAS_OTHER).
167 EVENT_DURATION,
168 EVENT_PHASE,
169 EVENT_CATEGORY,
170 EVENT_NAME,
171 EVENT_HAS_ARG,
172 EVENT_ARG,
173 // Return true if associated event exists.
174 // (Typically BEGIN for END or END for BEGIN).
175 EVENT_HAS_OTHER,
176 // Use these to access the associated event's members:
177 OTHER_PID,
178 OTHER_TID,
179 OTHER_TIME,
180 OTHER_PHASE,
181 OTHER_CATEGORY,
182 OTHER_NAME,
183 OTHER_HAS_ARG,
184 OTHER_ARG
185 };
186
187 class BASE_EXPORT Query {
188 public:
189 // Compare with the given member.
190 Query(TraceEventMember member);
191
192 // Compare with the given member argument value.
193 Query(TraceEventMember member, const std::string& arg_name);
194
195 Query(const Query& query);
196
197 ~Query();
198
199 // Compare with the given string.
200 static Query String(const std::string& str);
201
202 // Compare with the given number.
203 static Query Double(double num);
204 static Query Int(int32 num);
205 static Query Uint(uint32 num);
206
207 // Compare with the given bool.
208 static Query Bool(bool boolean);
209
210 // Compare with the given phase.
211 static Query Phase(base::debug::TraceEventPhase phase);
212
213 // Compare with the given string pattern. Only works with == and != operators.
214 // Example: Query(EVENT_NAME) == Query::Pattern("MyEvent*")
215 static Query Pattern(const std::string& pattern);
216
217 // Common queries:
218
219 // Find BEGIN events that have a corresponding END event.
220 static Query MatchBeginWithEnd() {
221 return (Query(EVENT_PHASE) ==
222 Query::Phase(base::debug::TRACE_EVENT_PHASE_BEGIN)) &&
223 Query(EVENT_HAS_OTHER);
224 }
225
226 // Find END events that have a corresponding BEGIN event.
227 static Query MatchEndWithBegin2() {
228 return (Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_END) &&
229 Query(EVENT_HAS_OTHER);
230 }
231
232 // Find BEGIN events of given |name| which also have associated END events.
233 static Query MatchBeginName(const std::string& name) {
234 return (Query(EVENT_NAME) == name) && MatchBeginWithEnd();
235 }
236
237 // Match given Process ID and Thread ID.
238 static Query MatchPidTid(TraceEvent::PidTid pid_tid) {
239 return (Query(EVENT_PID) == Query::Int(pid_tid.pid)) &&
240 (Query(EVENT_TID) == Query::Int(pid_tid.tid));
241 }
242
243 // Match event pair that spans multiple threads.
244 static Query MatchCrossPidTid() {
245 return (Query(EVENT_PID) != Query(OTHER_PID)) ||
246 (Query(EVENT_TID) != Query(OTHER_TID));
247 }
248
249 // Boolean operators:
250 Query operator==(const Query& rhs) const;
251 Query operator!=(const Query& rhs) const;
252 Query operator< (const Query& rhs) const;
253 Query operator<=(const Query& rhs) const;
254 Query operator> (const Query& rhs) const;
255 Query operator>=(const Query& rhs) const;
256 Query operator&&(const Query& rhs) const;
257 Query operator||(const Query& rhs) const;
258 Query operator!() const;
259
260 // Arithmetic operators:
261 // Following operators are applied to double arguments:
262 Query operator+(const Query& rhs) const;
263 Query operator-(const Query& rhs) const;
264 Query operator*(const Query& rhs) const;
265 Query operator/(const Query& rhs) const;
266 Query operator-() const;
267 // Mod operates on int64 args (doubles are casted to int64 beforehand):
268 Query operator%(const Query& rhs) const;
269
270 // Return true if the given event matches this query tree.
271 // This is a recursive method that walks the query tree.
272 bool Evaluate(const TraceEvent& event) const;
273
274 private:
275 enum Operator {
276 OP_INVALID,
277 // Boolean operators:
278 OP_EQ,
279 OP_NE,
280 OP_LT,
281 OP_LE,
282 OP_GT,
283 OP_GE,
284 OP_AND,
285 OP_OR,
286 OP_NOT,
287 // Arithmetic operators:
288 OP_ADD,
289 OP_SUB,
290 OP_MUL,
291 OP_DIV,
292 OP_MOD,
293 OP_NEGATE
294 };
295
296 enum QueryType {
297 QUERY_BOOLEAN_OPERATOR,
298 QUERY_ARITHMETIC_OPERATOR,
299 QUERY_EVENT_MEMBER,
300 QUERY_NUMBER,
301 QUERY_STRING
302 };
303
304 // Compare with the given string.
305 Query(const std::string& str);
306
307 // Compare with the given number.
308 Query(double num);
309
310 // Construct a boolean Query that returns (left <binary_op> right).
311 Query(const Query& left, const Query& right, Operator binary_op);
312
313 // Construct a boolean Query that returns (<binary_op> left).
314 Query(const Query& left, Operator unary_op);
315
316 // Try to compare left_ against right_ based on operator_.
317 // If either left or right does not convert to double, false is returned.
318 // Otherwise, true is returned and |result| is set to the comparison result.
319 bool CompareAsDouble(const TraceEvent& event, bool* result) const;
320
321 // Try to compare left_ against right_ based on operator_.
322 // If either left or right does not convert to string, false is returned.
323 // Otherwise, true is returned and |result| is set to the comparison result.
324 bool CompareAsString(const TraceEvent& event, bool* result) const;
325
326 // Attempt to convert this Query to a double. On success, true is returned
327 // and the double value is stored in |num|.
328 bool GetAsDouble(const TraceEvent& event, double* num) const;
329
330 // Attempt to convert this Query to a string. On success, true is returned
331 // and the string value is stored in |str|.
332 bool GetAsString(const TraceEvent& event, std::string* str) const;
333
334 // Evaluate this Query as an arithmetic operator on left_ and right_.
335 bool EvaluateArithmeticOperator(const TraceEvent& event,
336 double* num) const;
337
338 // For QUERY_EVENT_MEMBER Query: attempt to get the value of the Query.
339 // The TraceValue will either be TRACE_TYPE_DOUBLE, TRACE_TYPE_STRING,
340 // or if requested member does not exist, it will be TRACE_TYPE_UNDEFINED.
341 base::debug::TraceValue GetMemberValue(const TraceEvent& event) const;
342
343 // Does this Query represent a value?
344 bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
345
346 bool is_unary_operator() const {
347 return operator_ == OP_NOT || operator_ == OP_NEGATE;
348 }
349
350 const Query& left() const;
351 const Query& right() const;
352
353 QueryType type_;
354 Operator operator_;
355 scoped_refptr<QueryNode> left_;
356 scoped_refptr<QueryNode> right_;
357 TraceEventMember member_;
358 double number_;
359 std::string string_;
360 bool is_pattern_;
361 };
362
363 // Implementation detail:
364 // QueryNode allows Query to store a ref-counted query tree.
365 class QueryNode : public base::RefCounted<QueryNode> {
366 public:
367 explicit QueryNode(const Query& query);
368 const Query& query() const { return query_; }
369
370 private:
371 friend class base::RefCounted<QueryNode>;
372 ~QueryNode();
373
374 Query query_;
375 };
376
377 // TraceAnalyzer helps tests search for trace events.
378 class BASE_EXPORT TraceAnalyzer {
379 public:
380 typedef std::vector<const TraceEvent*> TraceEventVector;
381
382 ~TraceAnalyzer();
383
384 // Use trace events from JSON string generated by tracing API.
385 // Returns non-NULL if the JSON is successfully parsed.
386 static TraceAnalyzer* Create(const std::string& json_events)
387 WARN_UNUSED_RESULT;
388
389 // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
390 // to access the associated event and enables Query(EVENT_DURATION).
391 // An end event will match the most recent begin event with the same name,
392 // category, process ID and thread ID. This matches what is shown in
393 // about:tracing.
394 void AssociateBeginEndEvents();
395
396 // AssociateEvents can be used to customize event associations by setting the
397 // other_event member of TraceEvent. This should be used to associate two
398 // INSTANT events.
399 //
400 // The assumptions are:
401 // - |first| events occur before |second| events.
402 // - the closest matching |second| event is the correct match.
403 //
404 // |first| - Eligible |first| events match this query.
405 // |second| - Eligible |second| events match this query.
406 // |match| - This query is run on the |first| event. The OTHER_* EventMember
407 // queries will point to an eligible |second| event. The query
408 // should evaluate to true if the |first|/|second| pair is a match.
409 //
410 // When a match is found, the pair will be associated by having their
411 // other_event member point to each other. AssociateEvents does not clear
412 // previous associations, so it is possible to associate multiple pairs of
413 // events by calling AssociateEvents more than once with different queries.
414 //
415 // NOTE: AssociateEvents will overwrite existing other_event associations if
416 // the queries pass for events that already had a previous association.
417 //
418 // After calling FindEvents or FindOneEvent, it is not allowed to call
419 // AssociateEvents again.
420 void AssociateEvents(const Query& first,
421 const Query& second,
422 const Query& match);
423
424 // Find all events that match query and replace output vector.
425 size_t FindEvents(const Query& query, TraceEventVector* output);
426
427 // Helper method: find first event that matches query
428 const TraceEvent* FindOneEvent(const Query& query);
429
430 const std::string& GetThreadName(const TraceEvent::PidTid& pid_tid);
431
432 private:
433 TraceAnalyzer();
434
435 bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
436
437 // Read metadata (thread names, etc) from events.
438 void ParseMetadata();
439
440 std::map<TraceEvent::PidTid, std::string> thread_names_;
441 std::vector<TraceEvent> raw_events_;
442 bool allow_assocation_changes_;
443
444 DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
445 };
446
447 } // namespace trace_analyzer
448
449 #endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698