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

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 ~TraceEvent();
114
115 bool SetFromJSON(const base::Value* event_value) WARN_UNUSED_RESULT;
116
117 bool operator< (const TraceEvent& rhs) const {
118 return timestamp < rhs.timestamp;
119 }
120
121 bool has_other_event() const { return other_event; }
122
123 // Returns absolute duration in microseconds between this event and other
124 // event. Returns false if has_other_event() is false.
125 bool GetAbsTimeToOtherEvent(double* duration) const;
126
127 // Return the argument value if it exists and it is a string.
128 bool GetArgAsString(const std::string& name, std::string* arg) const;
129 // Return the argument value if it exists and it is a number.
130 bool GetArgAsNumber(const std::string& name, double* arg) const;
131
132 // Process ID and Thread ID.
133 PidTid pid_tid;
134
135 // Time since epoch in microseconds.
136 // Stored as double to match its JSON representation.
137 double timestamp;
138
139 base::debug::TraceEventPhase phase;
140
141 std::string category;
142
143 std::string name;
144
145 // All numbers and bool values from TraceEvent args are cast to double.
146 // bool becomes 1.0 (true) or 0.0 (false).
147 std::map<std::string, double> arg_numbers;
148
149 std::map<std::string, std::string> arg_strings;
150
151 // The other event associated with this event (or NULL).
152 const TraceEvent* other_event;
153 };
154
155 // Pass these values to Query to compare with the corresponding member of a
156 // TraceEvent.
157 enum TraceEventMember {
158 EVENT_INVALID,
159 // Use these to access the event members:
160 EVENT_PID,
161 EVENT_TID,
162 // Return the timestamp of the event in microseconds since epoch.
163 EVENT_TIME,
164 // Return the duration of an event in seconds.
165 // Only works for events with associated BEGIN/END: Query(EVENT_HAS_OTHER).
166 EVENT_DURATION,
167 EVENT_PHASE,
168 EVENT_CATEGORY,
169 EVENT_NAME,
170 EVENT_HAS_ARG,
171 EVENT_ARG,
172 // Return true if associated event exists.
173 // (Typically BEGIN for END or END for BEGIN).
174 EVENT_HAS_OTHER,
175 // Use these to access the associated event's members:
176 OTHER_PID,
177 OTHER_TID,
178 OTHER_TIME,
179 OTHER_PHASE,
180 OTHER_CATEGORY,
181 OTHER_NAME,
182 OTHER_HAS_ARG,
183 OTHER_ARG
184 };
185
186 class Query {
187 public:
188 // Compare with the given member.
189 Query(TraceEventMember member);
190
191 // Compare with the given member argument value.
192 Query(TraceEventMember member, const std::string& arg_name);
193
194 // Compare with the given string.
195 Query(const std::string& str);
196 Query(const char* str);
Paweł Hajdan Jr. 2011/10/19 08:59:23 Why yet another duplicate ctor? There is an implic
jbates 2011/10/19 19:35:54 char* can be implicitly converted to bool or std::
Paweł Hajdan Jr. 2011/10/21 08:31:46 This is scary. I didn't know that, so I expect man
jbates 2011/10/21 17:54:44 What did you think happens with this code? char* p
Paweł Hajdan Jr. 2011/10/24 07:19:28 This is obvious, but the interaction of this and s
jbates 2011/10/25 23:47:06 Done.
197
198 // Compare with the given number.
199 Query(double num);
200 Query(int32 num);
201 Query(uint32 num);
Paweł Hajdan Jr. 2011/10/19 08:59:23 I don't really like it. Maybe Query should be dist
jbates 2011/10/19 19:35:54 That's a good idea, but it doesn't map with how qu
202
203 // Compare with the given bool.
204 Query(bool boolean);
205
206 // Compare with the given phase.
207 Query(base::debug::TraceEventPhase phase);
208
209 Query(const Query& query);
210
211 ~Query();
212
213 // Compare with the given string pattern. Only works with == and != operators.
214 // Example: Query(EVENT_NAME) == Query::Pattern("bla_*")
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) == base::debug::TRACE_EVENT_PHASE_BEGIN) &&
222 Query(EVENT_HAS_OTHER);
223 }
224
225 // Find END events that have a corresponding BEGIN event.
226 static Query MatchEndWithBegin() {
227 return (Query(EVENT_PHASE) == base::debug::TRACE_EVENT_PHASE_END) &&
228 Query(EVENT_HAS_OTHER);
229 }
230
231 // Find BEGIN events of given |name| which also have associated END events.
232 static Query MatchBeginName(const std::string& name) {
233 return (Query(EVENT_NAME) == name) && MatchBeginWithEnd();
234 }
235
236 // Match given Process ID and Thread ID.
237 static Query MatchPidTid(TraceEvent::PidTid pid_tid) {
238 return (Query(EVENT_PID) == pid_tid.pid) &&
239 (Query(EVENT_TID) == pid_tid.tid);
240 }
241
242 // Match event pair that spans multiple threads.
243 static Query MatchCrossPidTid() {
244 return (Query(EVENT_PID) != Query(OTHER_PID)) ||
245 (Query(EVENT_TID) != Query(OTHER_TID));
246 }
247
248 // Boolean operators:
249 Query operator==(const Query& rhs) const;
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;
258
259 // Arithmetic operators:
260 // Following operators are applied to double arguments:
261 Query operator+(const Query& rhs) const;
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;
266 // Mod operates on int64 args (doubles are casted to int64 beforehand):
267 Query operator%(const Query& rhs) const;
268
269 // Return true if the given event matches this query tree.
270 // This is a recursive method that walks the query tree.
271 bool Evaluate(const TraceEvent& event) const;
272
273 private:
274 enum Operator {
275 OP_INVALID,
276 // Boolean operators:
277 OP_EQ,
278 OP_NE,
279 OP_LT,
280 OP_LE,
281 OP_GT,
282 OP_GE,
283 OP_AND,
284 OP_OR,
285 OP_NOT,
286 // Arithmetic operators:
287 OP_ADD,
288 OP_SUB,
289 OP_MUL,
290 OP_DIV,
291 OP_MOD,
292 OP_NEGATE
293 };
294
295 enum QueryType {
296 QUERY_BOOLEAN_OPERATOR,
297 QUERY_ARITHMETIC_OPERATOR,
298 QUERY_EVENT_MEMBER,
299 QUERY_NUMBER,
300 QUERY_STRING
301 };
302
303 // Construct a boolean Query that returns (left <binary_op> right).
304 Query(const Query& left, const Query& right, Operator binary_op);
305
306 // Construct a boolean Query that returns (<binary_op> left).
307 Query(const Query& left, Operator unary_op);
308
309 // Try to compare left_ against right_ based on operator_.
310 // If either left or right does not convert to double, false is returned.
311 // Otherwise, true is returned and |result| is set to the comparison result.
312 bool CompareAsDouble(const TraceEvent& event, bool* result) const;
313
314 // Try to compare left_ against right_ based on operator_.
315 // If either left or right does not convert to string, false is returned.
316 // Otherwise, true is returned and |result| is set to the comparison result.
317 bool CompareAsString(const TraceEvent& event, bool* result) const;
318
319 // Attempt to convert this Query to a double. On success, true is returned
320 // and the double value is stored in |num|.
321 bool GetAsDouble(const TraceEvent& event, double* num) const;
322
323 // Attempt to convert this Query to a string. On success, true is returned
324 // and the string value is stored in |str|.
325 bool GetAsString(const TraceEvent& event, std::string* str) const;
326
327 // Evaluate this Query as an arithmetic operator on left_ and right_.
328 bool EvaluateArithmeticOperator(const TraceEvent& event,
329 double* num) const;
330
331 // For QUERY_EVENT_MEMBER Query: attempt to get the value of the Query.
332 // The TraceValue will either be TRACE_TYPE_DOUBLE, TRACE_TYPE_STRING,
333 // or if requested member does not exist, it will be TRACE_TYPE_UNDEFINED.
334 base::debug::TraceValue GetMemberValue(const TraceEvent& event) const;
335
336 // Does this Query represent a value?
337 bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
338
339 bool is_unary_operator() const {
340 return operator_ == OP_NOT || operator_ == OP_NEGATE;
341 }
342
343 const Query& left() const;
344 const Query& right() const;
345
346 QueryType type_;
347 Operator operator_;
348 scoped_refptr<QueryNode> left_;
349 scoped_refptr<QueryNode> right_;
350 TraceEventMember member_;
351 double number_;
352 std::string string_;
353 bool is_pattern_;
354 };
355
356 // Implementation detail:
357 // QueryNode allows Query to store a ref-counted query tree.
358 class QueryNode : public base::RefCounted<QueryNode> {
359 public:
360 explicit QueryNode(const Query& query);
361 const Query& query() const { return query_; }
362
363 private:
364 friend class base::RefCounted<QueryNode>;
365 ~QueryNode();
366
367 Query query_;
368 };
369
370 // TraceAnalyzer helps tests search for trace events.
371 class TraceAnalyzer {
372 public:
373 typedef std::vector<const TraceEvent*> TraceEventVector;
374
375 ~TraceAnalyzer();
376
377 // Use trace events from JSON string generated by tracing API.
378 // Returns non-NULL if the JSON is successfully parsed.
379 static TraceAnalyzer* Create(const std::string& json_events)
380 WARN_UNUSED_RESULT;
381
382 // Associate BEGIN and END events with each other. This allows Query(OTHER_*)
383 // to access the associated event and enables Query(EVENT_DURATION).
384 // An end event will match the most recent begin event with the same name,
385 // category, process ID and thread ID. This matches what is shown in
386 // about:tracing.
387 void AssociateBeginEndEvents();
388
389 // AssociateEvents can be used to customize event associations by setting the
390 // other_event member of TraceEvent. This should be used to associate two
391 // INSTANT events.
392 //
393 // The assumptions are:
394 // - |first| events occur before |second| events.
395 // - the closest matching |second| event is the correct match.
396 //
397 // |first| - Eligible |first| events match this query.
398 // |second| - Eligible |second| events match this query.
399 // |match| - This query is run on the |first| event. The OTHER_* EventMember
400 // queries will point to an eligible |second| event. The query
401 // should evaluate to true if the |first|/|second| pair is a match.
402 //
403 // When a match is found, the pair will be associated by having their
404 // other_event member point to each other. AssociateEvents does not clear
405 // previous associations, so it is possible to associate multiple pairs of
406 // events by calling AssociateEvents more than once with different queries.
407 //
408 // NOTE: AssociateEvents will overwrite existing other_event associations if
409 // the queries pass for events that already had a previous association.
410 //
411 // After calling FindEvents or FindOneEvent, it is not allowed to call
412 // AssociateEvents again.
413 void AssociateEvents(const Query& first,
414 const Query& second,
415 const Query& match);
416
417 // Find all events that match query and replace output vector.
418 size_t FindEvents(const Query& query, TraceEventVector* output);
419
420 // Helper method: find first event that matches query
421 const TraceEvent* FindOneEvent(const Query& query);
422
423 const std::string& GetThreadName(const TraceEvent::PidTid& pid_tid);
424
425 private:
426 TraceAnalyzer();
427
428 bool SetEvents(const std::string& json_events) WARN_UNUSED_RESULT;
429
430 // Read metadata (thread names, etc) from events.
431 void ParseMetadata();
432
433 std::map<TraceEvent::PidTid, std::string> thread_names_;
434 std::vector<TraceEvent> raw_events_;
435 bool allow_assocation_changes_;
436
437 DISALLOW_COPY_AND_ASSIGN(TraceAnalyzer);
438 };
439
440 } // namespace trace_analyzer
441
442 #endif // BASE_TEST_TRACE_EVENT_ANALYZER_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698