Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
|
nduca
2011/10/11 20:33:37
trace_event_analyzer.h
move tests of the analyzer
jbates
2011/10/12 22:35:20
Done.
| |
| 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::Query with TraceAnalyzer to search for specific trace events. | |
|
nduca
2011/10/11 20:33:37
Advertise the namespace that these are all in? I h
jbates
2011/10/12 22:35:20
Done.
| |
| 6 // Queries can be combined using boolean and comparison operators. | |
| 7 // | |
| 8 // Construct TraceAnalyzer with the json trace string retrieved from TraceLog: | |
| 9 // TraceAnalyzer analyzer(json_events); | |
|
nduca
2011/10/11 20:33:37
What happens if it fails to parse? Maybe a TraceAn
jbates
2011/10/12 22:35:20
This should be okay, because the parse would only
| |
| 10 // ConstEventPtrVector events; | |
|
nduca
2011/10/11 20:33:37
A blurb explaining the basic idea of trace analysi
jbates
2011/10/12 22:35:20
Done.
| |
| 11 // | |
| 12 // To find begin events called "my_event" with duration > 1 second: | |
| 13 // Query q = (Query(EVENT_NAME) == "my_event" && | |
| 14 // Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN && | |
| 15 // Query(EVENT_DURATION) > 1000000.0); | |
| 16 // analyzer.FindEvents(q, &events); | |
| 17 // | |
| 18 // By default, only begin/end events on the same thread will ever be associated. | |
| 19 // If the test needs to analyze events that begin and end on different threads, | |
|
nduca
2011/10/11 20:33:37
Recapping our verbal discussion, I think should ta
jbates
2011/10/12 22:35:20
Sounds good. On second look though, the trace_even
| |
| 20 // it can specify custom assocations. The typical procedure is to include a | |
| 21 // unique ID as one of the TRACE_EVENT arguments that only matches a single | |
| 22 // begin/end pair across all Chrome processes and threads. | |
| 23 // | |
| 24 // For example: | |
| 25 // [Thread 1] | |
| 26 // TRACE_EVENT_BEGIN1("test_latency", "timing1", "id", 3); | |
| 27 // [Later, on Thread 2] | |
| 28 // TRACE_EVENT_END1("test_latency", "timing1", "id", 3); | |
|
nduca
2011/10/11 20:33:37
This trace wont' even parse in the trace_event_vie
jbates
2011/10/12 22:35:20
Fixed the example to use TRACE_EVENT_INSTANT, whic
| |
| 29 // Then, to associate these begin/end pairs: | |
| 30 // Query begin(Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN); | |
| 31 // Query end(Query(EVENT_PHASE) == TRACE_EVENT_PHASE_END); | |
| 32 // Query match(Query(EVENT_NAME) == Query(OTHER_NAME) && | |
| 33 // Query(EVENT_ARG, "id") == Query(OTHER_ARG, "id")); | |
| 34 // analyzer.AssociateEvents(begin, end, match); | |
|
nduca
2011/10/11 20:33:37
Based on the first example, I was hoping we'd see
jbates
2011/10/12 22:35:20
You're right, it should have been there... Done.
| |
| 35 // Then you can search for "timing1" events and evaluate their durations. | |
| 36 | |
| 37 | |
| 38 #ifndef BASE_DEBUG_TRACE_EVENT_TEST_UTILS_H_ | |
| 39 #define BASE_DEBUG_TRACE_EVENT_TEST_UTILS_H_ | |
| 40 #pragma once | |
| 41 | |
| 42 #include <map> | |
| 43 | |
| 44 #include "base/debug/trace_event.h" | |
| 45 | |
| 46 namespace base { | |
| 47 class Value; | |
| 48 | |
| 49 namespace debug { | |
| 50 | |
| 51 namespace trace { | |
| 52 class QueryNode; | |
| 53 } | |
| 54 | |
| 55 // TestTraceEvent is a more convenient form of the TraceEvent class to make | |
| 56 // tracing-based tests easier to write. | |
| 57 struct TestTraceEvent { | |
|
nduca
2011/10/11 20:33:37
I'm confused about base::trace vs base::debug::tra
jbates
2011/10/12 22:35:20
I've been thinking it would be great to move traci
| |
| 58 // PidTid contains a Process ID and Thread ID. | |
| 59 struct PidTid { | |
| 60 PidTid() : pid(0), tid(0) {} | |
| 61 PidTid(int pid, int tid) : pid(pid), tid(tid) {} | |
| 62 bool operator< (PidTid rhs) const { | |
| 63 if (pid != rhs.pid) | |
| 64 return pid < rhs.pid; | |
| 65 return tid < rhs.tid; | |
| 66 } | |
| 67 int pid; | |
| 68 int tid; | |
| 69 }; | |
| 70 | |
| 71 TestTraceEvent(); | |
| 72 TestTraceEvent(const base::Value* event_value); | |
| 73 ~TestTraceEvent(); | |
| 74 | |
| 75 // Convert JSON string to array of TestTraceEvent. | |
| 76 // |output| is appended with the parsed events. | |
| 77 static bool ParseEventsFromJson(const std::string& json, | |
|
nduca
2011/10/11 20:33:37
Makes more sense on traceanalyzer?
jbates
2011/10/12 22:35:20
This is only dependent on TestTraceEvent, so I thi
| |
| 78 std::vector<TestTraceEvent>* output); | |
| 79 | |
| 80 bool operator< (const TestTraceEvent& rhs) const { | |
| 81 return timestamp < rhs.timestamp; | |
| 82 } | |
| 83 | |
| 84 // Returns duration if it's available. | |
|
nduca
2011/10/11 20:33:37
Add a bool has_other_event() const;
Modify commen
jbates
2011/10/12 22:35:20
Done.
| |
| 85 bool GetDuration(double* duration) const; | |
| 86 | |
| 87 // Return the argument value if it exists. | |
| 88 bool IsArg(const std::string& name) const; | |
|
nduca
2011/10/11 20:33:37
HasArg
jbates
2011/10/12 22:35:20
Removed it - it wasn't used anywhere :)
| |
| 89 // Return the argument value if it exists and it is a string. | |
| 90 bool GetArgAsString(const std::string& name, std::string* arg) const; | |
| 91 // Return the argument value if it exists and it is a number. | |
| 92 bool GetArgAsNumber(const std::string& name, double* arg) const; | |
| 93 | |
| 94 // Called by TraceAnalyzer to associate this event with another event. | |
|
nduca
2011/10/11 20:33:37
Use friending so that this isn't visible?
jbates
2011/10/12 22:35:20
Also removed, it was unnecessary.
| |
| 95 void set_other_event(const TestTraceEvent* event) { | |
| 96 other_event = event; | |
| 97 } | |
| 98 | |
| 99 // Process ID and Thread ID. | |
|
nduca
2011/10/11 20:33:37
Are these really supposed to be public? Makes me a
jbates
2011/10/12 22:35:20
This is for tests, so I think it's best to keep it
| |
| 100 PidTid pid_tid; | |
| 101 // Time since epoch in microseconds. | |
| 102 // Stored as double to match its JSON representation. | |
| 103 double timestamp; | |
| 104 TraceEventPhase phase; | |
| 105 std::string category; | |
| 106 std::string name; | |
| 107 // All numbers and bool values from TraceEvent args are cast to double. | |
| 108 // bool becomes 1.0 (true) or 0.0 (false). | |
| 109 std::map<std::string, double> arg_numbers; | |
| 110 std::map<std::string, std::string> arg_strings; | |
| 111 // The other event associated with this event (or NULL). | |
| 112 const TestTraceEvent* other_event; | |
| 113 }; | |
| 114 | |
| 115 // By using the trace namespace, tests can use short terms like "Query". | |
| 116 namespace trace { | |
| 117 | |
| 118 // Pass these values to Query to compare with the corresponding member of a | |
| 119 // TestTraceEvent. | |
| 120 enum TraceEventMember { | |
| 121 EVENT_INVALID, | |
| 122 // Use these to access the event members: | |
| 123 EVENT_PID, | |
| 124 EVENT_TID, | |
| 125 // Return the timestamp of the event in microseconds since epoch. | |
| 126 EVENT_TIME, | |
| 127 // Return the duration of an event in seconds. | |
| 128 // Only works for events with associated BEGIN/END: Query(OTHER_EXISTS). | |
| 129 EVENT_DURATION, | |
| 130 EVENT_PHASE, | |
| 131 EVENT_CATEGORY, | |
| 132 EVENT_NAME, | |
| 133 EVENT_HAS_ARG, | |
| 134 EVENT_ARG, | |
| 135 // Return true if associated event exists. | |
| 136 // (Typically BEGIN for END or END for BEGIN). | |
| 137 EVENT_HAS_OTHER, | |
| 138 // Use these to access the associated event's members: | |
| 139 OTHER_PID, | |
| 140 OTHER_TID, | |
| 141 OTHER_TIME, | |
| 142 OTHER_PHASE, | |
| 143 OTHER_CATEGORY, | |
| 144 OTHER_NAME, | |
| 145 OTHER_HAS_ARG, | |
| 146 OTHER_ARG | |
| 147 }; | |
| 148 | |
| 149 class Query { | |
| 150 public: | |
| 151 // Compare with the given member. | |
| 152 Query(TraceEventMember member); | |
| 153 | |
| 154 // Compare with the given member argument value. | |
| 155 Query(TraceEventMember member, const std::string& arg_name); | |
| 156 | |
| 157 // Compare with the given string. | |
| 158 Query(const std::string& str); | |
| 159 Query(const char* str); | |
| 160 | |
| 161 // Compare with the given number. | |
| 162 Query(double num); | |
| 163 Query(float num); | |
| 164 Query(int num); | |
| 165 Query(uint32 num); | |
| 166 | |
| 167 // Compare with the given bool. | |
| 168 Query(bool boolean); | |
| 169 | |
| 170 // Compare with the given phase. | |
| 171 Query(TraceEventPhase phase); | |
| 172 | |
| 173 Query(const Query& query); | |
| 174 | |
| 175 ~Query(); | |
| 176 | |
| 177 // Compare with the given string pattern. Only works with == and != operators. | |
| 178 // Example: Query(EVENT_NAME) == Query::Pattern("bla_*") | |
| 179 static Query Pattern(const std::string& pattern); | |
| 180 | |
| 181 // Common queries: | |
| 182 | |
| 183 // Find BEGIN events that have a corresponding END event. | |
| 184 static Query MatchBeginWithEnd() { | |
| 185 return (Query(EVENT_PHASE) == TRACE_EVENT_PHASE_BEGIN) && | |
| 186 Query(EVENT_HAS_OTHER); | |
| 187 } | |
| 188 | |
| 189 // Find END events that have a corresponding BEGIN event. | |
| 190 static Query MatchEndWithBegin() { | |
| 191 return (Query(EVENT_PHASE) == TRACE_EVENT_PHASE_END) && | |
| 192 Query(EVENT_HAS_OTHER); | |
| 193 } | |
| 194 | |
| 195 // Find BEGIN events of given |name| which also have associated END events. | |
| 196 static Query MatchBeginName(const std::string& name) { | |
| 197 return (Query(EVENT_NAME) == name) && MatchBeginWithEnd(); | |
| 198 } | |
| 199 | |
| 200 // Match given Process ID and Thread ID. | |
| 201 static Query MatchPidTid(base::debug::TestTraceEvent::PidTid pid_tid) { | |
| 202 return (Query(EVENT_PID) == pid_tid.pid) && | |
| 203 (Query(EVENT_TID) == pid_tid.tid); | |
| 204 } | |
| 205 | |
| 206 // Match BEGIN/END event pair that spans multiple threads. | |
| 207 static Query MatchCrossPidTid() { | |
| 208 return (Query(EVENT_PID) != Query(OTHER_PID)) || | |
| 209 (Query(EVENT_TID) != Query(OTHER_TID)); | |
| 210 } | |
| 211 | |
| 212 // Boolean operators: | |
| 213 Query operator==(const Query& rhs) const; | |
| 214 Query operator!=(const Query& rhs) const; | |
| 215 Query operator< (const Query& rhs) const; | |
| 216 Query operator<=(const Query& rhs) const; | |
| 217 Query operator> (const Query& rhs) const; | |
| 218 Query operator>=(const Query& rhs) const; | |
| 219 Query operator&&(const Query& rhs) const; | |
| 220 Query operator||(const Query& rhs) const; | |
| 221 Query operator! () const; | |
| 222 | |
| 223 // Arithmetic operators: | |
| 224 Query operator+(const Query& rhs) const; | |
| 225 Query operator-(const Query& rhs) const; | |
| 226 Query operator*(const Query& rhs) const; | |
| 227 Query operator/(const Query& rhs) const; | |
| 228 Query operator%(const Query& rhs) const; | |
| 229 Query operator-() const; | |
| 230 | |
| 231 // Return true if the given event matches this query tree. | |
| 232 // This is a recursive method that walks the query tree. | |
| 233 bool Evaluate(const TestTraceEvent& event) const; | |
| 234 | |
| 235 private: | |
| 236 enum Operator { | |
| 237 OP_INVALID, | |
| 238 // Boolean operators: | |
| 239 OP_EQ, | |
| 240 OP_NE, | |
| 241 OP_LT, | |
| 242 OP_LE, | |
| 243 OP_GT, | |
| 244 OP_GE, | |
| 245 OP_AND, | |
| 246 OP_OR, | |
| 247 OP_NOT, | |
| 248 // Arithmetic operators: | |
| 249 OP_ADD, | |
| 250 OP_SUB, | |
| 251 OP_MUL, | |
| 252 OP_DIV, | |
| 253 OP_MOD, | |
| 254 OP_NEGATE | |
| 255 }; | |
| 256 | |
| 257 enum QueryType { | |
| 258 QUERY_BOOLEAN_OPERATOR, | |
| 259 QUERY_ARITHMETIC_OPERATOR, | |
| 260 QUERY_EVENT_MEMBER, | |
| 261 QUERY_NUMBER, | |
| 262 QUERY_STRING | |
| 263 }; | |
| 264 | |
| 265 // Construct a boolean Query that returns (left <binary_op> right). | |
| 266 Query(const Query& left, const Query& right, Operator binary_op); | |
| 267 | |
| 268 // Construct a boolean Query that returns (<binary_op> left). | |
| 269 Query(const Query& left, Operator unary_op); | |
| 270 | |
| 271 // Try to compare left_ against right_ based on operator_. | |
| 272 // If either left or right does not convert to double, false is returned. | |
| 273 // Otherwise, true is returned and |result| is set to the comparison result. | |
| 274 bool CompareAsDouble(const TestTraceEvent& event, bool* result) const; | |
| 275 | |
| 276 // Try to compare left_ against right_ based on operator_. | |
| 277 // If either left or right does not convert to string, false is returned. | |
| 278 // Otherwise, true is returned and |result| is set to the comparison result. | |
| 279 bool CompareAsString(const TestTraceEvent& event, bool* result) const; | |
| 280 | |
| 281 // Attempt to convert this Query to a double. On success, true is returned | |
| 282 // and the double value is stored in |num|. | |
| 283 bool GetAsDouble(const TestTraceEvent& event, double* num) const; | |
| 284 | |
| 285 // Attempt to convert this Query to a string. On success, true is returned | |
| 286 // and the string value is stored in |str|. | |
| 287 bool GetAsString(const TestTraceEvent& event, std::string* str) const; | |
| 288 | |
| 289 // Evaluate this Query as an arithmetic operator on left_ and right_. | |
| 290 bool EvaluateArithmeticOperator(const TestTraceEvent& event, | |
| 291 double* num) const; | |
| 292 | |
| 293 // For QUERY_EVENT_MEMBER Query: attempt to get the value of the Query. | |
| 294 // The TraceValue will either be TRACE_TYPE_DOUBLE, TRACE_TYPE_STRING, | |
| 295 // or if requested member does not exist, it will be TRACE_TYPE_UNDEFINED. | |
| 296 TraceValue GetMemberValue(const TestTraceEvent& event) const; | |
| 297 | |
| 298 // Does this Query represent a value? | |
| 299 bool is_value() const { return type_ != QUERY_BOOLEAN_OPERATOR; } | |
| 300 | |
| 301 bool is_unary_operator() const { | |
| 302 return operator_ == OP_NOT || operator_ == OP_NEGATE; | |
| 303 } | |
| 304 | |
| 305 const Query& left() const; | |
| 306 const Query& right() const; | |
| 307 | |
| 308 QueryType type_; | |
| 309 Operator operator_; | |
| 310 scoped_refptr<QueryNode> left_; | |
| 311 scoped_refptr<QueryNode> right_; | |
| 312 TraceEventMember member_; | |
| 313 double number_; | |
| 314 std::string string_; | |
| 315 bool is_pattern_; | |
| 316 }; | |
| 317 | |
| 318 // QueryNode allows Query to store a ref-counted query tree. | |
| 319 class QueryNode : public RefCounted<QueryNode> { | |
| 320 public: | |
| 321 QueryNode(const Query& query); | |
| 322 const Query& query() const { return query_; } | |
| 323 | |
| 324 private: | |
| 325 friend class RefCounted<QueryNode>; | |
| 326 ~QueryNode(); | |
| 327 | |
| 328 Query query_; | |
| 329 }; | |
| 330 | |
| 331 } // namespace trace | |
| 332 | |
| 333 // TraceAnalyzer is designed to make tracing-based tests easier to write. | |
| 334 class TraceAnalyzer { | |
| 335 public: | |
| 336 typedef std::vector<base::debug::TestTraceEvent> EventVector; | |
| 337 typedef std::vector<const base::debug::TestTraceEvent*> ConstEventPtrVector; | |
| 338 | |
| 339 TraceAnalyzer(); | |
| 340 TraceAnalyzer(const std::string& json_events); | |
| 341 TraceAnalyzer(const EventVector& events); | |
| 342 ~TraceAnalyzer(); | |
| 343 | |
| 344 // Replace all events with |json_events|. | |
| 345 void SetEvents(const std::string& json_events); | |
|
nduca
2011/10/11 20:33:37
Why this rather than creating another analyzer?
jbates
2011/10/12 22:35:20
The code is about the same either way - may as wel
| |
| 346 // Replace all events with |events|. | |
| 347 void SetEvents(const EventVector& events); | |
| 348 | |
| 349 // SetEvents calls this internally to match up typical begin/end pairs of | |
| 350 // events. This allows Query(OTHER_*) to access the associated event and | |
| 351 // enables Query(EVENT_DURATION). | |
| 352 // By default, an end event will match the most recent begin event with the | |
| 353 // same name, category, process ID and thread ID. | |
| 354 void SetDefaultAssociations(); | |
|
nduca
2011/10/11 20:33:37
Confused what this does. Does an end-user writing
jbates
2011/10/12 22:35:20
For now I think it makes sense to keep it availabl
| |
| 355 | |
| 356 // Clear existing event associations. | |
| 357 void ClearAssociations(); | |
|
nduca
2011/10/11 20:33:37
Same here... what does this do? Is the idea here t
jbates
2011/10/12 22:35:20
Correct -- AssociateEvents can be called multiple
| |
| 358 | |
| 359 // By default, matching begin and end events are associated with each other as | |
| 360 // described in SetDefaultAssociations. | |
| 361 // AssociateEvents can be used to customize begin/end event associations. | |
| 362 // The only assumption is that end events occur after begin events. | |
| 363 // | |
| 364 // |begin| - Eligible begin events match this query. | |
| 365 // |end| - Eligible end events match this query. | |
| 366 // |match| - This query is run on the begin event. The OTHER event members | |
| 367 // will point to an eligible end event. The query should evaluate to | |
| 368 // true if the begin/end pair is a match. | |
| 369 // | |
| 370 // When a match is found, the pair will be associated by having their | |
| 371 // other_event member point to each other. Non-matching events are left with | |
| 372 // their existing assocations, so you may also want to call ClearAssociations. | |
| 373 void AssociateEvents(const trace::Query& begin, | |
| 374 const trace::Query& end, | |
| 375 const trace::Query& match); | |
| 376 | |
|
nduca
2011/10/11 20:33:37
If we had B/E only work on threads, always using
jbates
2011/10/12 22:35:20
It might, but as we discussed, I'd like to start s
| |
| 377 const EventVector& events() { return raw_events_; } | |
| 378 | |
| 379 const std::string& GetThreadName(const base::debug::TestTraceEvent::PidTid& | |
| 380 pid_tid); | |
| 381 | |
| 382 // Find all events that match query and replace output vector. | |
| 383 size_t FindEvents(const trace::Query& query, | |
| 384 ConstEventPtrVector* output) const; | |
| 385 | |
| 386 // Helper method: find first event that matches query | |
| 387 const base::debug::TestTraceEvent* FindEvent( | |
| 388 const trace::Query& query) const; | |
| 389 | |
| 390 private: | |
| 391 // Read metadata (thread names, etc) from events. | |
| 392 void ParseMetadata(); | |
| 393 | |
| 394 std::map<base::debug::TestTraceEvent::PidTid, std::string> thread_names_; | |
| 395 EventVector raw_events_; | |
| 396 }; | |
| 397 | |
| 398 } // namespace debug | |
| 399 } // namespace base | |
| 400 | |
| 401 #endif // BASE_DEBUG_TRACE_EVENT_TEST_UTILS_H_ | |
| OLD | NEW |