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

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: . Created 9 years, 2 months 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) == "my_event" &&
43 // Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN &&
44 // Query(EVENT_DURATION) > 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) == "timing1_begin");
63 // Query end(Query(EVENT_NAME) == "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) == "timing1_begin" && Query(EVENT_HAS_OTHER));
69 // analyzer.FindEvents(q, &events);
70 //
71 // Step 4: analyze events, such as checking durations.
72 // for (size_t i = 0; i < events.size(); ++i) {
73 // double duration;
74 // EXPECT_TRUE(events[i].GetDuration(&duration));
75 // EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
76 // }
77
78
79 #ifndef BASE_TEST_TRACE_EVENT_ANALYZER_H_
80 #define BASE_TEST_TRACE_EVENT_ANALYZER_H_
81 #pragma once
82
83 #include <map>
84
85 #include "base/debug/trace_event.h"
86 #include "base/memory/ref_counted_memory.h"
87 #include "base/memory/scoped_ptr.h"
88
89 namespace base {
90 class Value;
91 }
92
93 namespace trace_analyzer {
94 class QueryNode;
95
96 // trace_analyzer::TraceEvent is a more convenient form of the
97 // base::debug::TraceEvent class to make tracing-based tests easier to write.
98 struct TraceEvent {
99 // PidTid contains a Process ID and Thread ID.
100 struct PidTid {
101 PidTid() : pid(0), tid(0) {}
102 PidTid(int pid, int tid) : pid(pid), tid(tid) {}
103 bool operator< (PidTid rhs) const {
104 if (pid != rhs.pid)
105 return pid < rhs.pid;
106 return tid < rhs.tid;
107 }
108 int pid;
109 int tid;
110 };
111
112 TraceEvent();
113 explicit TraceEvent(const base::Value* event_value);
114 ~TraceEvent();
115
116 bool operator< (const TraceEvent& rhs) const {
117 return timestamp < rhs.timestamp;
118 }
119
120 bool has_other_event() const { return other_event; }
121
122 // Returns duration in microseconds if has_other_event() is true.
123 bool GetDuration(double* duration) const;
nduca 2011/10/14 23:14:16 duration makes sense on begin events, but for two
jbates 2011/10/18 18:14:22 Renamed to GetAbsTimeToOtherEvent and documented t
124
125 // Return the argument value if it exists and it is a string.
126 bool GetArgAsString(const std::string& name, std::string* arg) const;
127 // Return the argument value if it exists and it is a number.
128 bool GetArgAsNumber(const std::string& name, double* arg) const;
129
130 // Process ID and Thread ID.
131 PidTid pid_tid;
nduca 2011/10/14 23:14:16 Do we typically add spaces between fields? I forge
jbates 2011/10/18 18:14:22 Done.
132 // Time since epoch in microseconds.
133 // Stored as double to match its JSON representation.
134 double timestamp;
135 base::debug::TraceEventPhase phase;
136 std::string category;
137 std::string name;
138 // All numbers and bool values from TraceEvent args are cast to double.
139 // bool becomes 1.0 (true) or 0.0 (false).
140 std::map<std::string, double> arg_numbers;
141 std::map<std::string, std::string> arg_strings;
142 // The other event associated with this event (or NULL).
143 const TraceEvent* other_event;
144 };
145
146 // Pass these values to Query to compare with the corresponding member of a
147 // TraceEvent.
148 enum TraceEventMember {
149 EVENT_INVALID,
150 // Use these to access the event members:
151 EVENT_PID,
152 EVENT_TID,
153 // Return the timestamp of the event in microseconds since epoch.
154 EVENT_TIME,
155 // Return the duration of an event in seconds.
156 // Only works for events with associated BEGIN/END: Query(EVENT_HAS_OTHER).
157 EVENT_DURATION,
158 EVENT_PHASE,
159 EVENT_CATEGORY,
160 EVENT_NAME,
161 EVENT_HAS_ARG,
162 EVENT_ARG,
163 // Return true if associated event exists.
164 // (Typically BEGIN for END or END for BEGIN).
165 EVENT_HAS_OTHER,
166 // Use these to access the associated event's members:
167 OTHER_PID,
168 OTHER_TID,
169 OTHER_TIME,
170 OTHER_PHASE,
171 OTHER_CATEGORY,
172 OTHER_NAME,
173 OTHER_HAS_ARG,
174 OTHER_ARG
175 };
176
177 class Query {
178 public:
179 // Compare with the given member.
180 Query(TraceEventMember member);
181
182 // Compare with the given member argument value.
183 Query(TraceEventMember member, const std::string& arg_name);
184
185 // Compare with the given string.
186 Query(const std::string& str);
187 Query(const char* str);
188
189 // Compare with the given number.
190 Query(double num);
Paweł Hajdan Jr. 2011/10/18 10:10:50 Having an override for both double and float seems
jbates 2011/10/18 18:14:22 Done. FYI, this was safe because the float constru
191 Query(float num);
192 Query(int num);
Paweł Hajdan Jr. 2011/10/18 10:10:50 Same here for int and uint, and bool... when liter
jbates 2011/10/18 18:14:22 These three are all distinct types, so literals sh
193 Query(uint32 num);
194
195 // Compare with the given bool.
196 Query(bool boolean);
197
198 // Compare with the given phase.
199 Query(base::debug::TraceEventPhase phase);
200
201 Query(const Query& query);
202
203 ~Query();
204
205 // Compare with the given string pattern. Only works with == and != operators.
206 // Example: Query(EVENT_NAME) == Query::Pattern("bla_*")
207 static Query Pattern(const std::string& pattern);
208
209 // Common queries:
210
211 // Find BEGIN events that have a corresponding END event.
212 static Query MatchBeginWithEnd() {
213 return (Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_BEGIN) &&
214 Query(EVENT_HAS_OTHER);
215 }
216
217 // Find END events that have a corresponding BEGIN event.
218 static Query MatchEndWithBegin() {
219 return (Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_END) &&
220 Query(EVENT_HAS_OTHER);
221 }
222
223 // Find BEGIN events of given |name| which also have associated END events.
224 static Query MatchBeginName(const std::string& name) {
225 return (Query(EVENT_NAME) == name) && MatchBeginWithEnd();
226 }
227
228 // Match given Process ID and Thread ID.
229 static Query MatchPidTid(TraceEvent::PidTid pid_tid) {
230 return (Query(EVENT_PID) == pid_tid.pid) &&
231 (Query(EVENT_TID) == pid_tid.tid);
232 }
233
234 // Match event pair that spans multiple threads.
235 static Query MatchCrossPidTid() {
236 return (Query(EVENT_PID) != Query(OTHER_PID)) ||
237 (Query(EVENT_TID) != Query(OTHER_TID));
238 }
239
240 // Boolean operators:
241 Query operator==(const Query& rhs) const;
Paweł Hajdan Jr. 2011/10/18 10:07:15 I think you get a response that there is no except
Paweł Hajdan Jr. 2011/10/18 10:10:50 Ah, just realized you said Brett is OK with this.
242 Query operator!=(const Query& rhs) const;
243 Query operator< (const Query& rhs) const;
244 Query operator<=(const Query& rhs) const;
245 Query operator> (const Query& rhs) const;
246 Query operator>=(const Query& rhs) const;
247 Query operator&&(const Query& rhs) const;
248 Query operator||(const Query& rhs) const;
249 Query operator!() const;
250
251 // Arithmetic operators:
252 // Following operators are applied to double arguments:
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;
258 // Mod operates on int64 args (doubles are casted to int64 beforehand):
259 Query operator%(const Query& rhs) const;
260
261 // Return true if the given event matches this query tree.
262 // This is a recursive method that walks the query tree.
263 bool Evaluate(const TraceEvent& event) const;
264
265 private:
266 enum Operator {
267 OP_INVALID,
268 // Boolean operators:
269 OP_EQ,
270 OP_NE,
271 OP_LT,
272 OP_LE,
273 OP_GT,
274 OP_GE,
275 OP_AND,
276 OP_OR,
277 OP_NOT,
278 // Arithmetic operators:
279 OP_ADD,
280 OP_SUB,
281 OP_MUL,
282 OP_DIV,
283 OP_MOD,
284 OP_NEGATE
285 };
286
287 enum QueryType {
288 QUERY_BOOLEAN_OPERATOR,
289 QUERY_ARITHMETIC_OPERATOR,
290 QUERY_EVENT_MEMBER,
291 QUERY_NUMBER,
292 QUERY_STRING
293 };
294
295 // Construct a boolean Query that returns (left <binary_op> right).
296 Query(const Query& left, const Query& right, Operator binary_op);
297
298 // Construct a boolean Query that returns (<binary_op> left).
299 Query(const Query& left, Operator unary_op);
300
301 // Try to compare left_ against right_ based on operator_.
302 // If either left or right does not convert to double, false is returned.
303 // Otherwise, true is returned and |result| is set to the comparison result.
304 bool CompareAsDouble(const TraceEvent& event, bool* result) const;
305
306 // Try to compare left_ against right_ based on operator_.
307 // If either left or right does not convert to string, false is returned.
308 // Otherwise, true is returned and |result| is set to the comparison result.
309 bool CompareAsString(const TraceEvent& event, bool* result) const;
310
311 // Attempt to convert this Query to a double. On success, true is returned
312 // and the double value is stored in |num|.
313 bool GetAsDouble(const TraceEvent& event, double* num) const;
314
315 // Attempt to convert this Query to a string. On success, true is returned
316 // and the string value is stored in |str|.
317 bool GetAsString(const TraceEvent& event, std::string* str) const;
318
319 // Evaluate this Query as an arithmetic operator on left_ and right_.
320 bool EvaluateArithmeticOperator(const TraceEvent& event,
321 double* num) const;
322
323 // For QUERY_EVENT_MEMBER Query: attempt to get the value of the Query.
324 // The TraceValue will either be TRACE_TYPE_DOUBLE, TRACE_TYPE_STRING,
325 // or if requested member does not exist, it will be TRACE_TYPE_UNDEFINED.
326 base::debug::TraceValue GetMemberValue(const TraceEvent& event) const;
327
328 // Does this Query represent a value?
329 bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
330
331 bool is_unary_operator() const {
332 return operator_ == OP_NOT || operator_ == OP_NEGATE;
333 }
334
335 const Query& left() const;
336 const Query& right() const;
337
338 QueryType type_;
339 Operator operator_;
340 scoped_refptr<QueryNode> left_;
341 scoped_refptr<QueryNode> right_;
342 TraceEventMember member_;
343 double number_;
344 std::string string_;
345 bool is_pattern_;
346 };
347
348 // Implementation detail:
349 // QueryNode allows Query to store a ref-counted query tree.
350 class QueryNode : public base::RefCounted<QueryNode> {
351 public:
352 explicit QueryNode(const Query& query);
353 const Query& query() const { return query_; }
354
355 private:
356 friend class base::RefCounted<QueryNode>;
357 ~QueryNode();
358
359 Query query_;
360 };
361
362 // TraceAnalyzer helps tests search for trace events.
363 class TraceAnalyzer {
364 public:
365 typedef std::vector<const TraceEvent*> TraceEventVector;
366
367 TraceAnalyzer();
368 ~TraceAnalyzer();
369
370 // Set trace events from JSON string generated by tracing API.
371 // Returns true if the JSON is successfully parsed.
372 bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
nduca 2011/10/14 23:14:16 What happens if you call this twice?
jbates 2011/10/18 18:14:22 Done.
373
374 // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
375 // to access the associated event and enables Query(EVENT_DURATION).
376 // An end event will match the most recent begin event with the same name,
377 // category, process ID and thread ID. This matches what is shown in
378 // about:tracing.
379 void AssociateBeginEndEvents();
380
381 // AssociateEvents can be used to customize begin/end event associations.
nduca 2011/10/14 23:14:16 does this work on begin/end events? I thought you
jbates 2011/10/18 18:14:22 Filed bug, improved comments. See related comment
382 // The assumptions are:
383 // - end events occur after begin events.
384 // - the closest matching end event is the best match.
385 //
386 // |begin| - Eligible begin events match this query.
387 // |end| - Eligible end events match this query.
388 // |match| - This query is run on the begin event. The OTHER event members
389 // will point to an eligible end event. The query should evaluate to
390 // true if the begin/end pair is a match.
391 //
392 // When a match is found, the pair will be associated by having their
393 // other_event member point to each other. AssociateEvents does not clear
394 // previous associations, so it is possible to associate multiple pairs of
395 // events by calling AssociateEvents more than once with different queries.
396 //
397 // After calling FindEvents or FindOneEvent, it is not allowed to call
398 // AssociateEvents again.
399 void AssociateEvents(const Query& begin,
400 const Query& end,
401 const Query& match);
402
403 // Find all events that match query and replace output vector.
404 size_t FindEvents(const Query& query, TraceEventVector* output);
405
406 // Helper method: find first event that matches query
407 const TraceEvent* FindOneEvent(const Query& query);
408
409 const std::string& GetThreadName(const TraceEvent::PidTid& pid_tid);
410
411 private:
412 // Read metadata (thread names, etc) from events.
413 void ParseMetadata();
414
415 std::map<TraceEvent::PidTid, std::string> thread_names_;
416 std::vector<TraceEvent> raw_events_;
417 bool allow_assocation_changes_;
418
419 DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
420 };
421
422 } // namespace trace_analyzer
423
424 #endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698