1 // Copyright (c) 2012 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.
5 #include "base/test/trace_event_analyzer.h"
11 #include "base/json/json_reader.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/strings/pattern.h"
14 #include "base/values.h"
16 namespace trace_analyzer
{
20 TraceEvent::TraceEvent()
24 phase(TRACE_EVENT_PHASE_BEGIN
),
28 TraceEvent::~TraceEvent() {
31 bool TraceEvent::SetFromJSON(const base::Value
* event_value
) {
32 if (event_value
->GetType() != base::Value::TYPE_DICTIONARY
) {
33 LOG(ERROR
) << "Value must be TYPE_DICTIONARY";
36 const base::DictionaryValue
* dictionary
=
37 static_cast<const base::DictionaryValue
*>(event_value
);
39 std::string phase_str
;
40 const base::DictionaryValue
* args
= NULL
;
42 if (!dictionary
->GetString("ph", &phase_str
)) {
43 LOG(ERROR
) << "ph is missing from TraceEvent JSON";
47 phase
= *phase_str
.data();
49 bool may_have_duration
= (phase
== TRACE_EVENT_PHASE_COMPLETE
);
50 bool require_origin
= (phase
!= TRACE_EVENT_PHASE_METADATA
);
51 bool require_id
= (phase
== TRACE_EVENT_PHASE_ASYNC_BEGIN
||
52 phase
== TRACE_EVENT_PHASE_ASYNC_STEP_INTO
||
53 phase
== TRACE_EVENT_PHASE_ASYNC_STEP_PAST
||
54 phase
== TRACE_EVENT_PHASE_ASYNC_END
);
56 if (require_origin
&& !dictionary
->GetInteger("pid", &thread
.process_id
)) {
57 LOG(ERROR
) << "pid is missing from TraceEvent JSON";
60 if (require_origin
&& !dictionary
->GetInteger("tid", &thread
.thread_id
)) {
61 LOG(ERROR
) << "tid is missing from TraceEvent JSON";
64 if (require_origin
&& !dictionary
->GetDouble("ts", ×tamp
)) {
65 LOG(ERROR
) << "ts is missing from TraceEvent JSON";
68 if (may_have_duration
) {
69 dictionary
->GetDouble("dur", &duration
);
71 if (!dictionary
->GetString("cat", &category
)) {
72 LOG(ERROR
) << "cat is missing from TraceEvent JSON";
75 if (!dictionary
->GetString("name", &name
)) {
76 LOG(ERROR
) << "name is missing from TraceEvent JSON";
79 if (!dictionary
->GetDictionary("args", &args
)) {
80 LOG(ERROR
) << "args is missing from TraceEvent JSON";
83 if (require_id
&& !dictionary
->GetString("id", &id
)) {
84 LOG(ERROR
) << "id is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
88 // For each argument, copy the type and create a trace_analyzer::TraceValue.
89 for (base::DictionaryValue::Iterator
it(*args
); !it
.IsAtEnd();
94 double double_num
= 0.0;
95 if (it
.value().GetAsString(&str
)) {
96 arg_strings
[it
.key()] = str
;
97 } else if (it
.value().GetAsInteger(&int_num
)) {
98 arg_numbers
[it
.key()] = static_cast<double>(int_num
);
99 } else if (it
.value().GetAsBoolean(&boolean
)) {
100 arg_numbers
[it
.key()] = static_cast<double>(boolean
? 1 : 0);
101 } else if (it
.value().GetAsDouble(&double_num
)) {
102 arg_numbers
[it
.key()] = double_num
;
104 LOG(WARNING
) << "Value type of argument is not supported: " <<
105 static_cast<int>(it
.value().GetType());
106 continue; // Skip non-supported arguments.
113 double TraceEvent::GetAbsTimeToOtherEvent() const {
114 return fabs(other_event
->timestamp
- timestamp
);
117 bool TraceEvent::GetArgAsString(const std::string
& name
,
118 std::string
* arg
) const {
119 std::map
<std::string
, std::string
>::const_iterator i
= arg_strings
.find(name
);
120 if (i
!= arg_strings
.end()) {
127 bool TraceEvent::GetArgAsNumber(const std::string
& name
,
129 std::map
<std::string
, double>::const_iterator i
= arg_numbers
.find(name
);
130 if (i
!= arg_numbers
.end()) {
137 bool TraceEvent::HasStringArg(const std::string
& name
) const {
138 return (arg_strings
.find(name
) != arg_strings
.end());
141 bool TraceEvent::HasNumberArg(const std::string
& name
) const {
142 return (arg_numbers
.find(name
) != arg_numbers
.end());
145 std::string
TraceEvent::GetKnownArgAsString(const std::string
& name
) const {
146 std::string arg_string
;
147 bool result
= GetArgAsString(name
, &arg_string
);
152 double TraceEvent::GetKnownArgAsDouble(const std::string
& name
) const {
153 double arg_double
= 0;
154 bool result
= GetArgAsNumber(name
, &arg_double
);
159 int TraceEvent::GetKnownArgAsInt(const std::string
& name
) const {
160 double arg_double
= 0;
161 bool result
= GetArgAsNumber(name
, &arg_double
);
163 return static_cast<int>(arg_double
);
166 bool TraceEvent::GetKnownArgAsBool(const std::string
& name
) const {
167 double arg_double
= 0;
168 bool result
= GetArgAsNumber(name
, &arg_double
);
170 return (arg_double
!= 0.0);
175 QueryNode::QueryNode(const Query
& query
) : query_(query
) {
178 QueryNode::~QueryNode() {
183 Query::Query(TraceEventMember member
)
184 : type_(QUERY_EVENT_MEMBER
),
185 operator_(OP_INVALID
),
191 Query::Query(TraceEventMember member
, const std::string
& arg_name
)
192 : type_(QUERY_EVENT_MEMBER
),
193 operator_(OP_INVALID
),
200 Query::Query(const Query
& query
)
201 : type_(query
.type_
),
202 operator_(query
.operator_
),
204 right_(query
.right_
),
205 member_(query
.member_
),
206 number_(query
.number_
),
207 string_(query
.string_
),
208 is_pattern_(query
.is_pattern_
) {
214 Query
Query::String(const std::string
& str
) {
218 Query
Query::Double(double num
) {
222 Query
Query::Int(int32 num
) {
223 return Query(static_cast<double>(num
));
226 Query
Query::Uint(uint32 num
) {
227 return Query(static_cast<double>(num
));
230 Query
Query::Bool(bool boolean
) {
231 return Query(boolean
? 1.0 : 0.0);
234 Query
Query::Phase(char phase
) {
235 return Query(static_cast<double>(phase
));
238 Query
Query::Pattern(const std::string
& pattern
) {
239 Query
query(pattern
);
240 query
.is_pattern_
= true;
244 bool Query::Evaluate(const TraceEvent
& event
) const {
245 // First check for values that can convert to bool.
247 // double is true if != 0:
248 double bool_value
= 0.0;
249 bool is_bool
= GetAsDouble(event
, &bool_value
);
251 return (bool_value
!= 0.0);
253 // string is true if it is non-empty:
254 std::string str_value
;
255 bool is_str
= GetAsString(event
, &str_value
);
257 return !str_value
.empty();
259 DCHECK_EQ(QUERY_BOOLEAN_OPERATOR
, type_
)
260 << "Invalid query: missing boolean expression";
262 DCHECK(right_
.get() || is_unary_operator());
264 if (is_comparison_operator()) {
265 DCHECK(left().is_value() && right().is_value())
266 << "Invalid query: comparison operator used between event member and "
268 bool compare_result
= false;
269 if (CompareAsDouble(event
, &compare_result
))
270 return compare_result
;
271 if (CompareAsString(event
, &compare_result
))
272 return compare_result
;
275 // It's a logical operator.
278 return left().Evaluate(event
) && right().Evaluate(event
);
280 return left().Evaluate(event
) || right().Evaluate(event
);
282 return !left().Evaluate(event
);
289 bool Query::CompareAsDouble(const TraceEvent
& event
, bool* result
) const {
291 if (!left().GetAsDouble(event
, &lhs
) || !right().GetAsDouble(event
, &rhs
))
295 *result
= (lhs
== rhs
);
298 *result
= (lhs
!= rhs
);
301 *result
= (lhs
< rhs
);
304 *result
= (lhs
<= rhs
);
307 *result
= (lhs
> rhs
);
310 *result
= (lhs
>= rhs
);
318 bool Query::CompareAsString(const TraceEvent
& event
, bool* result
) const {
319 std::string lhs
, rhs
;
320 if (!left().GetAsString(event
, &lhs
) || !right().GetAsString(event
, &rhs
))
324 if (right().is_pattern_
)
325 *result
= base::MatchPattern(lhs
, rhs
);
326 else if (left().is_pattern_
)
327 *result
= base::MatchPattern(rhs
, lhs
);
329 *result
= (lhs
== rhs
);
332 if (right().is_pattern_
)
333 *result
= !base::MatchPattern(lhs
, rhs
);
334 else if (left().is_pattern_
)
335 *result
= !base::MatchPattern(rhs
, lhs
);
337 *result
= (lhs
!= rhs
);
340 *result
= (lhs
< rhs
);
343 *result
= (lhs
<= rhs
);
346 *result
= (lhs
> rhs
);
349 *result
= (lhs
>= rhs
);
357 bool Query::EvaluateArithmeticOperator(const TraceEvent
& event
,
359 DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR
, type_
);
361 DCHECK(right_
.get() || is_unary_operator());
363 double lhs
= 0, rhs
= 0;
364 if (!left().GetAsDouble(event
, &lhs
))
366 if (!is_unary_operator() && !right().GetAsDouble(event
, &rhs
))
383 *num
= static_cast<double>(static_cast<int64
>(lhs
) %
384 static_cast<int64
>(rhs
));
395 bool Query::GetAsDouble(const TraceEvent
& event
, double* num
) const {
397 case QUERY_ARITHMETIC_OPERATOR
:
398 return EvaluateArithmeticOperator(event
, num
);
399 case QUERY_EVENT_MEMBER
:
400 return GetMemberValueAsDouble(event
, num
);
409 bool Query::GetAsString(const TraceEvent
& event
, std::string
* str
) const {
411 case QUERY_EVENT_MEMBER
:
412 return GetMemberValueAsString(event
, str
);
421 bool Query::GetMemberValueAsDouble(const TraceEvent
& event
,
423 DCHECK_EQ(QUERY_EVENT_MEMBER
, type_
);
425 // This could be a request for a member of |event| or a member of |event|'s
426 // associated event. Store the target event in the_event:
427 const TraceEvent
* the_event
= (member_
< OTHER_PID
) ?
428 &event
: event
.other_event
;
430 // Request for member of associated event, but there is no associated event.
437 *num
= static_cast<double>(the_event
->thread
.process_id
);
441 *num
= static_cast<double>(the_event
->thread
.thread_id
);
445 *num
= the_event
->timestamp
;
448 if (!the_event
->has_other_event())
450 *num
= the_event
->GetAbsTimeToOtherEvent();
452 case EVENT_COMPLETE_DURATION
:
453 if (the_event
->phase
!= TRACE_EVENT_PHASE_COMPLETE
)
455 *num
= the_event
->duration
;
459 *num
= static_cast<double>(the_event
->phase
);
461 case EVENT_HAS_STRING_ARG
:
462 case OTHER_HAS_STRING_ARG
:
463 *num
= (the_event
->HasStringArg(string_
) ? 1.0 : 0.0);
465 case EVENT_HAS_NUMBER_ARG
:
466 case OTHER_HAS_NUMBER_ARG
:
467 *num
= (the_event
->HasNumberArg(string_
) ? 1.0 : 0.0);
471 // Search for the argument name and return its value if found.
472 std::map
<std::string
, double>::const_iterator num_i
=
473 the_event
->arg_numbers
.find(string_
);
474 if (num_i
== the_event
->arg_numbers
.end())
476 *num
= num_i
->second
;
479 case EVENT_HAS_OTHER
:
480 // return 1.0 (true) if the other event exists
481 *num
= event
.other_event
? 1.0 : 0.0;
488 bool Query::GetMemberValueAsString(const TraceEvent
& event
,
489 std::string
* str
) const {
490 DCHECK_EQ(QUERY_EVENT_MEMBER
, type_
);
492 // This could be a request for a member of |event| or a member of |event|'s
493 // associated event. Store the target event in the_event:
494 const TraceEvent
* the_event
= (member_
< OTHER_PID
) ?
495 &event
: event
.other_event
;
497 // Request for member of associated event, but there is no associated event.
504 *str
= the_event
->category
;
508 *str
= the_event
->name
;
512 *str
= the_event
->id
;
516 // Search for the argument name and return its value if found.
517 std::map
<std::string
, std::string
>::const_iterator str_i
=
518 the_event
->arg_strings
.find(string_
);
519 if (str_i
== the_event
->arg_strings
.end())
521 *str
= str_i
->second
;
529 Query::Query(const std::string
& str
)
530 : type_(QUERY_STRING
),
531 operator_(OP_INVALID
),
532 member_(EVENT_INVALID
),
538 Query::Query(double num
)
539 : type_(QUERY_NUMBER
),
540 operator_(OP_INVALID
),
541 member_(EVENT_INVALID
),
545 const Query
& Query::left() const {
546 return left_
->query();
549 const Query
& Query::right() const {
550 return right_
->query();
553 Query
Query::operator==(const Query
& rhs
) const {
554 return Query(*this, rhs
, OP_EQ
);
557 Query
Query::operator!=(const Query
& rhs
) const {
558 return Query(*this, rhs
, OP_NE
);
561 Query
Query::operator<(const Query
& rhs
) const {
562 return Query(*this, rhs
, OP_LT
);
565 Query
Query::operator<=(const Query
& rhs
) const {
566 return Query(*this, rhs
, OP_LE
);
569 Query
Query::operator>(const Query
& rhs
) const {
570 return Query(*this, rhs
, OP_GT
);
573 Query
Query::operator>=(const Query
& rhs
) const {
574 return Query(*this, rhs
, OP_GE
);
577 Query
Query::operator&&(const Query
& rhs
) const {
578 return Query(*this, rhs
, OP_AND
);
581 Query
Query::operator||(const Query
& rhs
) const {
582 return Query(*this, rhs
, OP_OR
);
585 Query
Query::operator!() const {
586 return Query(*this, OP_NOT
);
589 Query
Query::operator+(const Query
& rhs
) const {
590 return Query(*this, rhs
, OP_ADD
);
593 Query
Query::operator-(const Query
& rhs
) const {
594 return Query(*this, rhs
, OP_SUB
);
597 Query
Query::operator*(const Query
& rhs
) const {
598 return Query(*this, rhs
, OP_MUL
);
601 Query
Query::operator/(const Query
& rhs
) const {
602 return Query(*this, rhs
, OP_DIV
);
605 Query
Query::operator%(const Query
& rhs
) const {
606 return Query(*this, rhs
, OP_MOD
);
609 Query
Query::operator-() const {
610 return Query(*this, OP_NEGATE
);
614 Query::Query(const Query
& left
, const Query
& right
, Operator binary_op
)
615 : operator_(binary_op
),
616 left_(new QueryNode(left
)),
617 right_(new QueryNode(right
)),
618 member_(EVENT_INVALID
),
620 type_
= (binary_op
< OP_ADD
?
621 QUERY_BOOLEAN_OPERATOR
: QUERY_ARITHMETIC_OPERATOR
);
624 Query::Query(const Query
& left
, Operator unary_op
)
625 : operator_(unary_op
),
626 left_(new QueryNode(left
)),
627 member_(EVENT_INVALID
),
629 type_
= (unary_op
< OP_ADD
?
630 QUERY_BOOLEAN_OPERATOR
: QUERY_ARITHMETIC_OPERATOR
);
635 // Search |events| for |query| and add matches to |output|.
636 size_t FindMatchingEvents(const std::vector
<TraceEvent
>& events
,
638 TraceEventVector
* output
,
639 bool ignore_metadata_events
) {
640 for (size_t i
= 0; i
< events
.size(); ++i
) {
641 if (ignore_metadata_events
&& events
[i
].phase
== TRACE_EVENT_PHASE_METADATA
)
643 if (query
.Evaluate(events
[i
]))
644 output
->push_back(&events
[i
]);
646 return output
->size();
649 bool ParseEventsFromJson(const std::string
& json
,
650 std::vector
<TraceEvent
>* output
) {
651 scoped_ptr
<base::Value
> root
;
652 root
.reset(base::JSONReader::DeprecatedRead(json
));
654 base::ListValue
* root_list
= NULL
;
655 if (!root
.get() || !root
->GetAsList(&root_list
))
658 for (size_t i
= 0; i
< root_list
->GetSize(); ++i
) {
659 base::Value
* item
= NULL
;
660 if (root_list
->Get(i
, &item
)) {
662 if (event
.SetFromJSON(item
))
663 output
->push_back(event
);
676 TraceAnalyzer::TraceAnalyzer()
677 : ignore_metadata_events_(false),
678 allow_assocation_changes_(true) {}
680 TraceAnalyzer::~TraceAnalyzer() {
684 TraceAnalyzer
* TraceAnalyzer::Create(const std::string
& json_events
) {
685 scoped_ptr
<TraceAnalyzer
> analyzer(new TraceAnalyzer());
686 if (analyzer
->SetEvents(json_events
))
687 return analyzer
.release();
691 bool TraceAnalyzer::SetEvents(const std::string
& json_events
) {
693 if (!ParseEventsFromJson(json_events
, &raw_events_
))
695 std::stable_sort(raw_events_
.begin(), raw_events_
.end());
700 void TraceAnalyzer::AssociateBeginEndEvents() {
701 using trace_analyzer::Query
;
703 Query
begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN
));
704 Query
end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END
));
705 Query
match(Query::EventName() == Query::OtherName() &&
706 Query::EventCategory() == Query::OtherCategory() &&
707 Query::EventTid() == Query::OtherTid() &&
708 Query::EventPid() == Query::OtherPid());
710 AssociateEvents(begin
, end
, match
);
713 void TraceAnalyzer::AssociateAsyncBeginEndEvents() {
714 using trace_analyzer::Query
;
717 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN
) ||
718 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO
) ||
719 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST
));
720 Query
end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END
) ||
721 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO
) ||
722 Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST
));
723 Query
match(Query::EventName() == Query::OtherName() &&
724 Query::EventCategory() == Query::OtherCategory() &&
725 Query::EventId() == Query::OtherId());
727 AssociateEvents(begin
, end
, match
);
730 void TraceAnalyzer::AssociateEvents(const Query
& first
,
732 const Query
& match
) {
733 DCHECK(allow_assocation_changes_
)
734 << "AssociateEvents not allowed after FindEvents";
736 // Search for matching begin/end event pairs. When a matching end is found,
737 // it is associated with the begin event.
738 std::vector
<TraceEvent
*> begin_stack
;
739 for (size_t event_index
= 0; event_index
< raw_events_
.size();
742 TraceEvent
& this_event
= raw_events_
[event_index
];
744 if (second
.Evaluate(this_event
)) {
745 // Search stack for matching begin, starting from end.
746 for (int stack_index
= static_cast<int>(begin_stack
.size()) - 1;
747 stack_index
>= 0; --stack_index
) {
748 TraceEvent
& begin_event
= *begin_stack
[stack_index
];
750 // Temporarily set other to test against the match query.
751 const TraceEvent
* other_backup
= begin_event
.other_event
;
752 begin_event
.other_event
= &this_event
;
753 if (match
.Evaluate(begin_event
)) {
754 // Found a matching begin/end pair.
755 // Erase the matching begin event index from the stack.
756 begin_stack
.erase(begin_stack
.begin() + stack_index
);
760 // Not a match, restore original other and continue.
761 begin_event
.other_event
= other_backup
;
764 // Even if this_event is a |second| event that has matched an earlier
765 // |first| event, it can still also be a |first| event and be associated
766 // with a later |second| event.
767 if (first
.Evaluate(this_event
)) {
768 begin_stack
.push_back(&this_event
);
773 void TraceAnalyzer::MergeAssociatedEventArgs() {
774 for (size_t i
= 0; i
< raw_events_
.size(); ++i
) {
775 // Merge all associated events with the first event.
776 const TraceEvent
* other
= raw_events_
[i
].other_event
;
777 // Avoid looping by keeping set of encountered TraceEvents.
778 std::set
<const TraceEvent
*> encounters
;
779 encounters
.insert(&raw_events_
[i
]);
780 while (other
&& encounters
.find(other
) == encounters
.end()) {
781 encounters
.insert(other
);
782 raw_events_
[i
].arg_numbers
.insert(
783 other
->arg_numbers
.begin(),
784 other
->arg_numbers
.end());
785 raw_events_
[i
].arg_strings
.insert(
786 other
->arg_strings
.begin(),
787 other
->arg_strings
.end());
788 other
= other
->other_event
;
793 size_t TraceAnalyzer::FindEvents(const Query
& query
, TraceEventVector
* output
) {
794 allow_assocation_changes_
= false;
796 return FindMatchingEvents(
797 raw_events_
, query
, output
, ignore_metadata_events_
);
800 const TraceEvent
* TraceAnalyzer::FindFirstOf(const Query
& query
) {
801 TraceEventVector output
;
802 if (FindEvents(query
, &output
) > 0)
803 return output
.front();
807 const TraceEvent
* TraceAnalyzer::FindLastOf(const Query
& query
) {
808 TraceEventVector output
;
809 if (FindEvents(query
, &output
) > 0)
810 return output
.back();
814 const std::string
& TraceAnalyzer::GetThreadName(
815 const TraceEvent::ProcessThreadID
& thread
) {
816 // If thread is not found, just add and return empty string.
817 return thread_names_
[thread
];
820 void TraceAnalyzer::ParseMetadata() {
821 for (size_t i
= 0; i
< raw_events_
.size(); ++i
) {
822 TraceEvent
& this_event
= raw_events_
[i
];
823 // Check for thread name metadata.
824 if (this_event
.phase
!= TRACE_EVENT_PHASE_METADATA
||
825 this_event
.name
!= "thread_name")
827 std::map
<std::string
, std::string
>::const_iterator string_it
=
828 this_event
.arg_strings
.find("name");
829 if (string_it
!= this_event
.arg_strings
.end())
830 thread_names_
[this_event
.thread
] = string_it
->second
;
834 // TraceEventVector utility functions.
836 bool GetRateStats(const TraceEventVector
& events
,
838 const RateStatsOptions
* options
) {
840 // Need at least 3 events to calculate rate stats.
841 const size_t kMinEvents
= 3;
842 if (events
.size() < kMinEvents
) {
843 LOG(ERROR
) << "Not enough events: " << events
.size();
847 std::vector
<double> deltas
;
848 size_t num_deltas
= events
.size() - 1;
849 for (size_t i
= 0; i
< num_deltas
; ++i
) {
850 double delta
= events
.at(i
+ 1)->timestamp
- events
.at(i
)->timestamp
;
852 LOG(ERROR
) << "Events are out of order";
855 deltas
.push_back(delta
);
858 std::sort(deltas
.begin(), deltas
.end());
861 if (options
->trim_min
+ options
->trim_max
> events
.size() - kMinEvents
) {
862 LOG(ERROR
) << "Attempt to trim too many events";
865 deltas
.erase(deltas
.begin(), deltas
.begin() + options
->trim_min
);
866 deltas
.erase(deltas
.end() - options
->trim_max
, deltas
.end());
869 num_deltas
= deltas
.size();
870 double delta_sum
= 0.0;
871 for (size_t i
= 0; i
< num_deltas
; ++i
)
872 delta_sum
+= deltas
[i
];
874 stats
->min_us
= *std::min_element(deltas
.begin(), deltas
.end());
875 stats
->max_us
= *std::max_element(deltas
.begin(), deltas
.end());
876 stats
->mean_us
= delta_sum
/ static_cast<double>(num_deltas
);
878 double sum_mean_offsets_squared
= 0.0;
879 for (size_t i
= 0; i
< num_deltas
; ++i
) {
880 double offset
= fabs(deltas
[i
] - stats
->mean_us
);
881 sum_mean_offsets_squared
+= offset
* offset
;
883 stats
->standard_deviation_us
=
884 sqrt(sum_mean_offsets_squared
/ static_cast<double>(num_deltas
- 1));
889 bool FindFirstOf(const TraceEventVector
& events
,
892 size_t* return_index
) {
893 DCHECK(return_index
);
894 for (size_t i
= position
; i
< events
.size(); ++i
) {
895 if (query
.Evaluate(*events
[i
])) {
903 bool FindLastOf(const TraceEventVector
& events
,
906 size_t* return_index
) {
907 DCHECK(return_index
);
908 for (size_t i
= std::min(position
+ 1, events
.size()); i
!= 0; --i
) {
909 if (query
.Evaluate(*events
[i
- 1])) {
910 *return_index
= i
- 1;
917 bool FindClosest(const TraceEventVector
& events
,
920 size_t* return_closest
,
921 size_t* return_second_closest
) {
922 DCHECK(return_closest
);
923 if (events
.empty() || position
>= events
.size())
925 size_t closest
= events
.size();
926 size_t second_closest
= events
.size();
927 for (size_t i
= 0; i
< events
.size(); ++i
) {
928 if (!query
.Evaluate(*events
.at(i
)))
930 if (closest
== events
.size()) {
934 if (fabs(events
.at(i
)->timestamp
- events
.at(position
)->timestamp
) <
935 fabs(events
.at(closest
)->timestamp
- events
.at(position
)->timestamp
)) {
936 second_closest
= closest
;
938 } else if (second_closest
== events
.size()) {
943 if (closest
< events
.size() &&
944 (!return_second_closest
|| second_closest
< events
.size())) {
945 *return_closest
= closest
;
946 if (return_second_closest
)
947 *return_second_closest
= second_closest
;
954 size_t CountMatches(const TraceEventVector
& events
,
956 size_t begin_position
,
957 size_t end_position
) {
958 if (begin_position
>= events
.size())
960 end_position
= (end_position
< events
.size()) ? end_position
: events
.size();
962 for (size_t i
= begin_position
; i
< end_position
; ++i
) {
963 if (query
.Evaluate(*events
.at(i
)))
969 } // namespace trace_analyzer