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

Side by Side Diff: base/debug/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
« no previous file with comments | « base/debug/trace_event.cc ('k') | base/debug/trace_event_analyzer.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 base::debug::trace::Query and base::debug::TraceAnalyzer to search for
6 // specific trace events that were generated by the trace_event.h API.
7 //
8 // The basic procedure:
9 // - Get trace events JSON string from base::debug::TraceLog.
nduca 2011/10/13 01:23:58 The more I think about it, a cleanup here is - rem
10 // - Pass the events string to TraceAnalyzer.
nduca 2011/10/13 01:23:58 Isn't there an optional step here before FindEvent
11 // - Call TraceAnalyzer::FindEvents with queries to find specific events.
12 //
13 // A Query is a boolean expression tree that evaluates to true or false for a
14 // given trace event. Queries can be combined into a tree using boolean,
15 // arithmetic and comparison operators that refer to data of an individual trace
16 // event.
17 //
18 // The events are returned as base::debug::TestTraceEvent objects.
19 // TestTraceEvent contains a single trace event's data, as well as a pointer to
20 // a related trace event. The related trace event is typically the matching end
21 // of a begin event or the matching begin of an end event.
22 //
23 // The following examples use this basic setup code to construct TraceAnalyzer
24 // with the json trace string retrieved from TraceLog and construct an event
25 // vector for retrieving events:
26 //
27 // TraceAnalyzer analyzer(json_events);
28 // ConstEventPtrVector events;
nduca 2011/10/13 01:23:58 When I see this sort of thing, its a sign somethin
29 //
30 // During construction, TraceAnalyzer::SetDefaultAssociations is called to
31 // associate all matching begin/end pairs similar to how they are shown in
32 // about:tracing.
33 //
34 // EXAMPLE 1: Find events named "my_event".
35 //
36 // analyzer.FindEvents(Query(EVENT_NAME) == "my_event", &events);
37 //
38 // EXAMPLE 2: Find begin events named "my_event" with duration > 1 second.
39 //
40 // Query q = (Query(EVENT_NAME) == "my_event" &&
41 // Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN &&
42 // Query(EVENT_DURATION) > 1000000.0);
43 // analyzer.FindEvents(q, &events);
44 //
45 // EXAMPLE 3: Matching begin/end events across threads.
46 //
47 // By default, only begin/end events on the same thread will ever be associated.
nduca 2011/10/13 01:23:58 I dont think begin or end events should *ever* be
48 // If the test needs to analyze events that begin and end on different threads,
49 // it can specify custom assocations. The typical procedure is to include a
50 // unique ID as one of the TRACE_EVENT arguments that only matches a single
51 // begin/end pair across all Chrome processes and threads.
52 //
53 // Step 1: instrument code with custom begin/end trace events.
54 // [Thread 1 tracing code]
55 // TRACE_EVENT_INSTANT1("test_latency", "timing1_begin", "id", 3);
56 // [Thread 2 tracing code]
57 // TRACE_EVENT_INSTANT1("test_latency", "timing1_end", "id", 3);
58 //
59 // Step 2: associate these custom begin/end pairs.
60 // Query begin(Query(EVENT_NAME) == "timing1_begin");
61 // Query end(Query(EVENT_NAME) == "timing1_end");
62 // Query match(Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id"));
63 // analyzer.AssociateEvents(begin, end, match);
64 //
65 // Step 3: search for "timing1_begin" events with existing other event.
66 // Query q = (Query(EVENT_NAME) == "timing1_begin" && Query(EVENT_HAS_OTHER));
67 // analyzer.FindEvents(q, &events);
68 //
69 // Step 4: analyze events, such as checking durations.
70 // for (size_t i = 0; i < events.size(); ++i) {
71 // double duration;
72 // EXPECT_TRUE(events[i].GetDuration(&duration));
73 // EXPECT_LT(duration, 1000000.0/60.0); // expect less than 1/60 second.
74 // }
75
76
77 #ifndef BASE_DEBUG_TRACE_EVENT_ANALYZER_H_
78 #define BASE_DEBUG_TRACE_EVENT_ANALYZER_H_
79 #pragma once
80
81 #include <map>
82
83 #include "base/debug/trace_event.h"
84
85 namespace base {
86 class Value;
87
88 namespace debug {
89
90 namespace trace {
91 class QueryNode;
92 }
93
94 // TestTraceEvent is a more convenient form of the TraceEvent class to make
95 // tracing-based tests easier to write.
96 struct TestTraceEvent {
nduca 2011/10/13 01:23:58 I think I mentioned this somewhere else but just c
97 // PidTid contains a Process ID and Thread ID.
98 struct PidTid {
99 PidTid() : pid(0), tid(0) {}
100 PidTid(int pid, int tid) : pid(pid), tid(tid) {}
101 bool operator< (PidTid rhs) const {
102 if (pid != rhs.pid)
103 return pid < rhs.pid;
104 return tid < rhs.tid;
105 }
106 int pid;
107 int tid;
108 };
109
110 TestTraceEvent();
111 TestTraceEvent(const base::Value* event_value);
112 ~TestTraceEvent();
113
114 // Convert JSON string to array of TestTraceEvent.
115 // |output| is appended with the parsed events.
116 static bool ParseEventsFromJson(const std::string& json,
117 std::vector<TestTraceEvent>* output);
118
119 bool operator< (const TestTraceEvent& rhs) const {
120 return timestamp < rhs.timestamp;
121 }
122
123 bool has_other_event() const { return other_event; }
124
125 // Returns duration if has_other_event() is true.
nduca 2011/10/13 01:23:58 Might make a note about the unit of the duration.
126 bool GetDuration(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 // Time since epoch in microseconds.
136 // Stored as double to match its JSON representation.
137 double timestamp;
138 TraceEventPhase phase;
139 std::string category;
140 std::string name;
141 // All numbers and bool values from TraceEvent args are cast to double.
142 // bool becomes 1.0 (true) or 0.0 (false).
143 std::map<std::string, double> arg_numbers;
144 std::map<std::string, std::string> arg_strings;
145 // The other event associated with this event (or NULL).
146 const TestTraceEvent* other_event;
147 };
148
149 // By using the trace namespace, tests can use short terms like "Query".
nduca 2011/10/13 01:23:58 Presumably this will change with the new namespace
150 namespace trace {
151
152 // Pass these values to Query to compare with the corresponding member of a
153 // TestTraceEvent.
154 enum TraceEventMember {
155 EVENT_INVALID,
156 // Use these to access the event members:
157 EVENT_PID,
158 EVENT_TID,
159 // Return the timestamp of the event in microseconds since epoch.
160 EVENT_TIME,
161 // Return the duration of an event in seconds.
162 // Only works for events with associated BEGIN/END: Query(EVENT_HAS_OTHER).
163 EVENT_DURATION,
164 EVENT_PHASE,
165 EVENT_CATEGORY,
166 EVENT_NAME,
167 EVENT_HAS_ARG,
168 EVENT_ARG,
169 // Return true if associated event exists.
170 // (Typically BEGIN for END or END for BEGIN).
171 EVENT_HAS_OTHER,
172 // Use these to access the associated event's members:
173 OTHER_PID,
174 OTHER_TID,
175 OTHER_TIME,
176 OTHER_PHASE,
177 OTHER_CATEGORY,
178 OTHER_NAME,
179 OTHER_HAS_ARG,
180 OTHER_ARG
181 };
182
183 class Query {
184 public:
185 // Compare with the given member.
186 Query(TraceEventMember member);
187
188 // Compare with the given member argument value.
189 Query(TraceEventMember member, const std::string& arg_name);
190
191 // Compare with the given string.
192 Query(const std::string& str);
193 Query(const char* str);
194
195 // Compare with the given number.
196 Query(double num);
197 Query(float num);
198 Query(int num);
199 Query(uint32 num);
200
201 // Compare with the given bool.
202 Query(bool boolean);
203
204 // Compare with the given phase.
205 Query(TraceEventPhase phase);
206
207 Query(const Query& query);
208
209 ~Query();
210
211 // Compare with the given string pattern. Only works with == and != operators.
212 // Example: Query(EVENT_NAME) == Query::Pattern("bla_*")
213 static Query Pattern(const std::string& pattern);
214
215 // Common queries:
216
217 // Find BEGIN events that have a corresponding END event.
218 static Query MatchBeginWithEnd() {
219 return (Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN) &&
220 Query(EVENT_HAS_OTHER);
221 }
222
223 // Find END events that have a corresponding BEGIN event.
224 static Query MatchEndWithBegin() {
225 return (Query(EVENT_PHASE) == TRACE_EVENT_PHASE_END) &&
226 Query(EVENT_HAS_OTHER);
227 }
228
229 // Find BEGIN events of given |name| which also have associated END events.
230 static Query MatchBeginName(const std::string& name) {
231 return (Query(EVENT_NAME) == name) && MatchBeginWithEnd();
232 }
233
234 // Match given Process ID and Thread ID.
235 static Query MatchPidTid(base::debug::TestTraceEvent::PidTid pid_tid) {
236 return (Query(EVENT_PID) == pid_tid.pid) &&
237 (Query(EVENT_TID) == pid_tid.tid);
238 }
239
240 // Match event pair that spans multiple threads.
241 static Query MatchCrossPidTid() {
242 return (Query(EVENT_PID) != Query(OTHER_PID)) ||
243 (Query(EVENT_TID) != Query(OTHER_TID));
244 }
245
246 // Boolean operators:
247 Query operator==(const Query& rhs) const;
248 Query operator!=(const Query& rhs) const;
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;
256
257 // Arithmetic operators:
258 // Following operators are applied to double arguments:
259 Query operator+(const Query& rhs) const;
260 Query operator-(const Query& rhs) const;
261 Query operator*(const Query& rhs) const;
262 Query operator/(const Query& rhs) const;
263 Query operator-() const;
264 // Mod operates on int64 args (doubles are casted to int64 beforehand):
265 Query operator%(const Query& rhs) const;
266
267 // Return true if the given event matches this query tree.
268 // This is a recursive method that walks the query tree.
269 bool Evaluate(const TestTraceEvent& event) const;
270
271 private:
272 enum Operator {
273 OP_INVALID,
274 // Boolean operators:
275 OP_EQ,
276 OP_NE,
277 OP_LT,
278 OP_LE,
279 OP_GT,
280 OP_GE,
281 OP_AND,
282 OP_OR,
283 OP_NOT,
284 // Arithmetic operators:
285 OP_ADD,
286 OP_SUB,
287 OP_MUL,
288 OP_DIV,
289 OP_MOD,
290 OP_NEGATE
291 };
292
293 enum QueryType {
294 QUERY_BOOLEAN_OPERATOR,
295 QUERY_ARITHMETIC_OPERATOR,
296 QUERY_EVENT_MEMBER,
297 QUERY_NUMBER,
298 QUERY_STRING
299 };
300
301 // Construct a boolean Query that returns (left <binary_op> right).
302 Query(const Query& left, const Query& right, Operator binary_op);
303
304 // Construct a boolean Query that returns (<binary_op> left).
305 Query(const Query& left, Operator unary_op);
306
307 // Try to compare left_ against right_ based on operator_.
308 // If either left or right does not convert to double, false is returned.
309 // Otherwise, true is returned and |result| is set to the comparison result.
310 bool CompareAsDouble(const TestTraceEvent& event, bool* result) const;
311
312 // Try to compare left_ against right_ based on operator_.
313 // If either left or right does not convert to string, false is returned.
314 // Otherwise, true is returned and |result| is set to the comparison result.
315 bool CompareAsString(const TestTraceEvent& event, bool* result) const;
316
317 // Attempt to convert this Query to a double. On success, true is returned
318 // and the double value is stored in |num|.
319 bool GetAsDouble(const TestTraceEvent& event, double* num) const;
320
321 // Attempt to convert this Query to a string. On success, true is returned
322 // and the string value is stored in |str|.
323 bool GetAsString(const TestTraceEvent& event, std::string* str) const;
324
325 // Evaluate this Query as an arithmetic operator on left_ and right_.
326 bool EvaluateArithmeticOperator(const TestTraceEvent& event,
327 double* num) const;
328
329 // For QUERY_EVENT_MEMBER Query: attempt to get the value of the Query.
330 // The TraceValue will either be TRACE_TYPE_DOUBLE, TRACE_TYPE_STRING,
331 // or if requested member does not exist, it will be TRACE_TYPE_UNDEFINED.
332 TraceValue GetMemberValue(const TestTraceEvent& event) const;
333
334 // Does this Query represent a value?
335 bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; }
336
337 bool is_unary_operator() const {
338 return operator_ == OP_NOT || operator_ == OP_NEGATE;
339 }
340
341 const Query& left() const;
342 const Query& right() const;
343
344 QueryType type_;
345 Operator operator_;
346 scoped_refptr<QueryNode> left_;
347 scoped_refptr<QueryNode> right_;
348 TraceEventMember member_;
349 double number_;
350 std::string string_;
351 bool is_pattern_;
352 };
353
354 // Implementation detail:
355 // QueryNode allows Query to store a ref-counted query tree.
356 class QueryNode : public RefCounted<QueryNode> {
357 public:
358 QueryNode(const Query& query);
359 const Query& query() const { return query_; }
360
361 private:
362 friend class RefCounted<QueryNode>;
363 ~QueryNode();
364
365 Query query_;
366 };
367
368 } // namespace trace
369
370 // TraceAnalyzer is designed to make tracing-based tests easier to write.
371 class TraceAnalyzer {
372 public:
373 typedef std::vector<base::debug::TestTraceEvent> EventVector;
374 typedef std::vector<const base::debug::TestTraceEvent*> ConstEventPtrVector;
375
376 TraceAnalyzer();
377 TraceAnalyzer(const std::string& json_events);
378 TraceAnalyzer(const EventVector& events);
nduca 2011/10/13 01:23:58 what uses this?
379 ~TraceAnalyzer();
380
381 // Replace all events with |json_events|.
382 void SetEvents(const std::string& json_events);
383 // Replace all events with |events|.
384 void SetEvents(const EventVector& events);
385
386 // SetEvents calls this internally to match up typical begin/end pairs of
387 // events. This allows Query(OTHER_*) to access the associated event and
388 // enables Query(EVENT_DURATION).
389 // By default, an end event will match the most recent begin event with the
390 // same name, category, process ID and thread ID.
391 void SetDefaultAssociations();
392
393 // Clear existing event associations.
394 void ClearAssociations();
395
396 // By default, matching begin and end events are associated with each other as
397 // described in SetDefaultAssociations.
398 // AssociateEvents can be used to customize begin/end event associations.
399 // The assumptions are:
400 // - end events occur after begin events.
401 // - the closest matching end event is the best match.
402 //
403 // |begin| - Eligible begin events match this query.
404 // |end| - Eligible end events match this query.
405 // |match| - This query is run on the begin event. The OTHER event members
406 // will point to an eligible end event. The query should evaluate to
407 // true if the begin/end pair is a match.
408 //
409 // When a match is found, the pair will be associated by having their
410 // other_event member point to each other. Non-matching events are left with
411 // their existing assocations, so you may also want to call ClearAssociations.
412 void AssociateEvents(const trace::Query& begin,
413 const trace::Query& end,
414 const trace::Query& match);
415
416 const EventVector& events() { return raw_events_; }
nduca 2011/10/13 01:23:58 Why expose this? Couldn't one do FindEvents(Query(
417
418 const std::string& GetThreadName(const base::debug::TestTraceEvent::PidTid&
419 pid_tid);
420
421 // Find all events that match query and replace output vector.
422 size_t FindEvents(const trace::Query& query,
423 ConstEventPtrVector* output) const;
424
425 // Helper method: find first event that matches query
426 const base::debug::TestTraceEvent* FindOneEvent(
427 const trace::Query& query) const;
428
429 private:
430 // Read metadata (thread names, etc) from events.
431 void ParseMetadata();
432
433 std::map<base::debug::TestTraceEvent::PidTid, std::string> thread_names_;
434 EventVector raw_events_;
435 };
436
437 } // namespace debug
438 } // namespace base
439
440 #endif // BASE_DEBUG_TRACE_EVENT_ANALYZER_H_
OLDNEW
« no previous file with comments | « base/debug/trace_event.cc ('k') | base/debug/trace_event_analyzer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698