OLD | NEW |
---|---|
(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_ | |
OLD | NEW |