12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/test/trace_event_analyzer.h"
- #include <math.h>
- #include <algorithm>
- #include <set>
- #include "base/bind.h"
- #include "base/json/json_reader.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/ref_counted_memory.h"
- #include "base/ranges/algorithm.h"
- #include "base/run_loop.h"
- #include "base/strings/pattern.h"
- #include "base/trace_event/trace_buffer.h"
- #include "base/trace_event/trace_config.h"
- #include "base/trace_event/trace_log.h"
- #include "base/values.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace {
- void OnTraceDataCollected(base::OnceClosure quit_closure,
- base::trace_event::TraceResultBuffer* buffer,
- const scoped_refptr<base::RefCountedString>& json,
- bool has_more_events) {
- buffer->AddFragment(json->data());
- if (!has_more_events)
- std::move(quit_closure).Run();
- }
- } // namespace
- namespace trace_analyzer {
- // TraceEvent
- TraceEvent::TraceEvent() : thread(0, 0) {}
- TraceEvent::TraceEvent(TraceEvent&& other) = default;
- TraceEvent::~TraceEvent() = default;
- TraceEvent& TraceEvent::operator=(TraceEvent&& rhs) = default;
- bool TraceEvent::SetFromJSON(const base::Value* event_value) {
- if (!event_value->is_dict()) {
- LOG(ERROR) << "Value must be Type::DICTIONARY";
- return false;
- }
- const base::Value::Dict& event_dict = event_value->GetDict();
- const std::string* maybe_phase = event_dict.FindString("ph");
- if (!maybe_phase) {
- LOG(ERROR) << "ph is missing from TraceEvent JSON";
- return false;
- }
- phase = *maybe_phase->data();
- bool may_have_duration = (phase == TRACE_EVENT_PHASE_COMPLETE);
- bool require_origin = (phase != TRACE_EVENT_PHASE_METADATA);
- bool require_id = (phase == TRACE_EVENT_PHASE_ASYNC_BEGIN ||
- phase == TRACE_EVENT_PHASE_ASYNC_STEP_INTO ||
- phase == TRACE_EVENT_PHASE_ASYNC_STEP_PAST ||
- phase == TRACE_EVENT_PHASE_MEMORY_DUMP ||
- phase == TRACE_EVENT_PHASE_CREATE_OBJECT ||
- phase == TRACE_EVENT_PHASE_DELETE_OBJECT ||
- phase == TRACE_EVENT_PHASE_SNAPSHOT_OBJECT ||
- phase == TRACE_EVENT_PHASE_ASYNC_END);
- if (require_origin) {
- absl::optional<int> maybe_process_id = event_dict.FindInt("pid");
- if (!maybe_process_id) {
- LOG(ERROR) << "pid is missing from TraceEvent JSON";
- return false;
- }
- thread.process_id = *maybe_process_id;
- absl::optional<int> maybe_thread_id = event_dict.FindInt("tid");
- if (!maybe_thread_id) {
- LOG(ERROR) << "tid is missing from TraceEvent JSON";
- return false;
- }
- thread.thread_id = *maybe_thread_id;
- absl::optional<double> maybe_timestamp = event_dict.FindDouble("ts");
- if (!maybe_timestamp) {
- LOG(ERROR) << "ts is missing from TraceEvent JSON";
- return false;
- }
- timestamp = *maybe_timestamp;
- }
- if (may_have_duration) {
- absl::optional<double> maybe_duration = event_dict.FindDouble("dur");
- if (maybe_duration)
- duration = *maybe_duration;
- }
- const std::string* maybe_category = event_dict.FindString("cat");
- if (!maybe_category) {
- LOG(ERROR) << "cat is missing from TraceEvent JSON";
- return false;
- }
- category = *maybe_category;
- const std::string* maybe_name = event_dict.FindString("name");
- if (!maybe_name) {
- LOG(ERROR) << "name is missing from TraceEvent JSON";
- return false;
- }
- name = *maybe_name;
- const base::Value::Dict* maybe_args = event_dict.FindDict("args");
- if (!maybe_args) {
- // If argument filter is enabled, the arguments field contains a string
- // value.
- const std::string* maybe_stripped_args = event_dict.FindString("args");
- if (!maybe_stripped_args || *maybe_stripped_args != "__stripped__") {
- LOG(ERROR) << "args is missing from TraceEvent JSON";
- return false;
- }
- }
- const base::Value::Dict* maybe_id2 = nullptr;
- if (require_id) {
- const std::string* maybe_id = event_dict.FindString("id");
- maybe_id2 = event_dict.FindDict("id2");
- if (!maybe_id && !maybe_id2) {
- LOG(ERROR)
- << "id/id2 is missing from ASYNC_BEGIN/ASYNC_END TraceEvent JSON";
- return false;
- }
- if (maybe_id)
- id = *maybe_id;
- }
- absl::optional<double> maybe_thread_duration = event_dict.FindDouble("tdur");
- if (maybe_thread_duration) {
- thread_duration = *maybe_thread_duration;
- }
- absl::optional<double> maybe_thread_timestamp = event_dict.FindDouble("tts");
- if (maybe_thread_timestamp) {
- thread_timestamp = *maybe_thread_timestamp;
- }
- const std::string* maybe_scope = event_dict.FindString("scope");
- if (maybe_scope) {
- scope = *maybe_scope;
- }
- const std::string* maybe_bind_id = event_dict.FindString("bind_id");
- if (maybe_bind_id) {
- bind_id = *maybe_bind_id;
- }
- absl::optional<bool> maybe_flow_out = event_dict.FindBool("flow_out");
- if (maybe_flow_out) {
- flow_out = *maybe_flow_out;
- }
- absl::optional<bool> maybe_flow_in = event_dict.FindBool("flow_in");
- if (maybe_flow_in) {
- flow_in = *maybe_flow_in;
- }
- if (maybe_id2) {
- const std::string* maybe_global_id2 = maybe_id2->FindString("global");
- if (maybe_global_id2) {
- global_id2 = *maybe_global_id2;
- }
- const std::string* maybe_local_id2 = maybe_id2->FindString("local");
- if (maybe_local_id2) {
- local_id2 = *maybe_local_id2;
- }
- }
- // For each argument, copy the type and create a trace_analyzer::TraceValue.
- // TODO(crbug.com/1303874): Add BINARY and LIST arg types if needed.
- if (maybe_args) {
- for (auto pair : *maybe_args) {
- switch (pair.second.type()) {
- case base::Value::Type::STRING:
- arg_strings[pair.first] = pair.second.GetString();
- break;
- case base::Value::Type::INTEGER:
- arg_numbers[pair.first] = static_cast<double>(pair.second.GetInt());
- break;
- case base::Value::Type::BOOLEAN:
- arg_numbers[pair.first] = pair.second.GetBool() ? 1.0 : 0.0;
- break;
- case base::Value::Type::DOUBLE:
- arg_numbers[pair.first] = pair.second.GetDouble();
- break;
- case base::Value::Type::DICT:
- arg_dicts[pair.first] = pair.second.GetDict().Clone();
- break;
- default:
- break;
- }
- }
- }
- return true;
- }
- double TraceEvent::GetAbsTimeToOtherEvent() const {
- return fabs(other_event->timestamp - timestamp);
- }
- bool TraceEvent::GetArgAsString(const std::string& arg_name,
- std::string* arg) const {
- const auto it = arg_strings.find(arg_name);
- if (it != arg_strings.end()) {
- *arg = it->second;
- return true;
- }
- return false;
- }
- bool TraceEvent::GetArgAsNumber(const std::string& arg_name,
- double* arg) const {
- const auto it = arg_numbers.find(arg_name);
- if (it != arg_numbers.end()) {
- *arg = it->second;
- return true;
- }
- return false;
- }
- bool TraceEvent::GetArgAsDict(const std::string& arg_name,
- base::Value::Dict* arg) const {
- const auto it = arg_dicts.find(arg_name);
- if (it != arg_dicts.end()) {
- *arg = it->second.Clone();
- return true;
- }
- return false;
- }
- bool TraceEvent::HasStringArg(const std::string& arg_name) const {
- return (arg_strings.find(arg_name) != arg_strings.end());
- }
- bool TraceEvent::HasNumberArg(const std::string& arg_name) const {
- return (arg_numbers.find(arg_name) != arg_numbers.end());
- }
- bool TraceEvent::HasDictArg(const std::string& arg_name) const {
- return (arg_dicts.find(arg_name) != arg_dicts.end());
- }
- std::string TraceEvent::GetKnownArgAsString(const std::string& arg_name) const {
- std::string arg_string;
- bool result = GetArgAsString(arg_name, &arg_string);
- DCHECK(result);
- return arg_string;
- }
- double TraceEvent::GetKnownArgAsDouble(const std::string& arg_name) const {
- double arg_double = 0;
- bool result = GetArgAsNumber(arg_name, &arg_double);
- DCHECK(result);
- return arg_double;
- }
- int TraceEvent::GetKnownArgAsInt(const std::string& arg_name) const {
- double arg_double = 0;
- bool result = GetArgAsNumber(arg_name, &arg_double);
- DCHECK(result);
- return static_cast<int>(arg_double);
- }
- bool TraceEvent::GetKnownArgAsBool(const std::string& arg_name) const {
- double arg_double = 0;
- bool result = GetArgAsNumber(arg_name, &arg_double);
- DCHECK(result);
- return (arg_double != 0.0);
- }
- base::Value::Dict TraceEvent::GetKnownArgAsDict(
- const std::string& arg_name) const {
- base::Value::Dict arg_dict;
- bool result = GetArgAsDict(arg_name, &arg_dict);
- DCHECK(result);
- return arg_dict;
- }
- // QueryNode
- QueryNode::QueryNode(const Query& query) : query_(query) {
- }
- QueryNode::~QueryNode() = default;
- // Query
- Query::Query(TraceEventMember member)
- : type_(QUERY_EVENT_MEMBER),
- operator_(OP_INVALID),
- member_(member),
- number_(0),
- is_pattern_(false) {
- }
- Query::Query(TraceEventMember member, const std::string& arg_name)
- : type_(QUERY_EVENT_MEMBER),
- operator_(OP_INVALID),
- member_(member),
- number_(0),
- string_(arg_name),
- is_pattern_(false) {
- }
- Query::Query(const Query& query) = default;
- Query::~Query() = default;
- Query Query::String(const std::string& str) {
- return Query(str);
- }
- Query Query::Double(double num) {
- return Query(num);
- }
- Query Query::Int(int32_t num) {
- return Query(static_cast<double>(num));
- }
- Query Query::Uint(uint32_t num) {
- return Query(static_cast<double>(num));
- }
- Query Query::Bool(bool boolean) {
- return Query(boolean ? 1.0 : 0.0);
- }
- Query Query::Phase(char phase) {
- return Query(static_cast<double>(phase));
- }
- Query Query::Pattern(const std::string& pattern) {
- Query query(pattern);
- query.is_pattern_ = true;
- return query;
- }
- bool Query::Evaluate(const TraceEvent& event) const {
- // First check for values that can convert to bool.
- // double is true if != 0:
- double bool_value = 0.0;
- bool is_bool = GetAsDouble(event, &bool_value);
- if (is_bool)
- return (bool_value != 0.0);
- // string is true if it is non-empty:
- std::string str_value;
- bool is_str = GetAsString(event, &str_value);
- if (is_str)
- return !str_value.empty();
- DCHECK_EQ(QUERY_BOOLEAN_OPERATOR, type_)
- << "Invalid query: missing boolean expression";
- DCHECK(left_.get());
- DCHECK(right_.get() || is_unary_operator());
- if (is_comparison_operator()) {
- DCHECK(left().is_value() && right().is_value())
- << "Invalid query: comparison operator used between event member and "
- "value.";
- bool compare_result = false;
- if (CompareAsDouble(event, &compare_result))
- return compare_result;
- if (CompareAsString(event, &compare_result))
- return compare_result;
- return false;
- }
- // It's a logical operator.
- switch (operator_) {
- case OP_AND:
- return left().Evaluate(event) && right().Evaluate(event);
- case OP_OR:
- return left().Evaluate(event) || right().Evaluate(event);
- case OP_NOT:
- return !left().Evaluate(event);
- default:
- NOTREACHED();
- return false;
- }
- }
- bool Query::CompareAsDouble(const TraceEvent& event, bool* result) const {
- double lhs, rhs;
- if (!left().GetAsDouble(event, &lhs) || !right().GetAsDouble(event, &rhs))
- return false;
- switch (operator_) {
- case OP_EQ:
- *result = (lhs == rhs);
- return true;
- case OP_NE:
- *result = (lhs != rhs);
- return true;
- case OP_LT:
- *result = (lhs < rhs);
- return true;
- case OP_LE:
- *result = (lhs <= rhs);
- return true;
- case OP_GT:
- *result = (lhs > rhs);
- return true;
- case OP_GE:
- *result = (lhs >= rhs);
- return true;
- default:
- NOTREACHED();
- return false;
- }
- }
- bool Query::CompareAsString(const TraceEvent& event, bool* result) const {
- std::string lhs, rhs;
- if (!left().GetAsString(event, &lhs) || !right().GetAsString(event, &rhs))
- return false;
- switch (operator_) {
- case OP_EQ:
- if (right().is_pattern_)
- *result = base::MatchPattern(lhs, rhs);
- else if (left().is_pattern_)
- *result = base::MatchPattern(rhs, lhs);
- else
- *result = (lhs == rhs);
- return true;
- case OP_NE:
- if (right().is_pattern_)
- *result = !base::MatchPattern(lhs, rhs);
- else if (left().is_pattern_)
- *result = !base::MatchPattern(rhs, lhs);
- else
- *result = (lhs != rhs);
- return true;
- case OP_LT:
- *result = (lhs < rhs);
- return true;
- case OP_LE:
- *result = (lhs <= rhs);
- return true;
- case OP_GT:
- *result = (lhs > rhs);
- return true;
- case OP_GE:
- *result = (lhs >= rhs);
- return true;
- default:
- NOTREACHED();
- return false;
- }
- }
- bool Query::EvaluateArithmeticOperator(const TraceEvent& event,
- double* num) const {
- DCHECK_EQ(QUERY_ARITHMETIC_OPERATOR, type_);
- DCHECK(left_.get());
- DCHECK(right_.get() || is_unary_operator());
- double lhs = 0, rhs = 0;
- if (!left().GetAsDouble(event, &lhs))
- return false;
- if (!is_unary_operator() && !right().GetAsDouble(event, &rhs))
- return false;
- switch (operator_) {
- case OP_ADD:
- *num = lhs + rhs;
- return true;
- case OP_SUB:
- *num = lhs - rhs;
- return true;
- case OP_MUL:
- *num = lhs * rhs;
- return true;
- case OP_DIV:
- *num = lhs / rhs;
- return true;
- case OP_MOD:
- *num = static_cast<double>(static_cast<int64_t>(lhs) %
- static_cast<int64_t>(rhs));
- return true;
- case OP_NEGATE:
- *num = -lhs;
- return true;
- default:
- NOTREACHED();
- return false;
- }
- }
- bool Query::GetAsDouble(const TraceEvent& event, double* num) const {
- switch (type_) {
- case QUERY_ARITHMETIC_OPERATOR:
- return EvaluateArithmeticOperator(event, num);
- case QUERY_EVENT_MEMBER:
- return GetMemberValueAsDouble(event, num);
- case QUERY_NUMBER:
- *num = number_;
- return true;
- default:
- return false;
- }
- }
- bool Query::GetAsString(const TraceEvent& event, std::string* str) const {
- switch (type_) {
- case QUERY_EVENT_MEMBER:
- return GetMemberValueAsString(event, str);
- case QUERY_STRING:
- *str = string_;
- return true;
- default:
- return false;
- }
- }
- const TraceEvent* Query::SelectTargetEvent(const TraceEvent* event,
- TraceEventMember member) {
- if (member >= OTHER_FIRST_MEMBER && member <= OTHER_LAST_MEMBER)
- return event->other_event;
- if (member >= PREV_FIRST_MEMBER && member <= PREV_LAST_MEMBER)
- return event->prev_event;
- return event;
- }
- bool Query::GetMemberValueAsDouble(const TraceEvent& event,
- double* num) const {
- DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
- // This could be a request for a member of |event| or a member of |event|'s
- // associated previous or next event. Store the target event in the_event:
- const TraceEvent* the_event = SelectTargetEvent(&event, member_);
- // Request for member of associated event, but there is no associated event.
- if (!the_event)
- return false;
- switch (member_) {
- case EVENT_PID:
- case OTHER_PID:
- case PREV_PID:
- *num = static_cast<double>(the_event->thread.process_id);
- return true;
- case EVENT_TID:
- case OTHER_TID:
- case PREV_TID:
- *num = static_cast<double>(the_event->thread.thread_id);
- return true;
- case EVENT_TIME:
- case OTHER_TIME:
- case PREV_TIME:
- *num = the_event->timestamp;
- return true;
- case EVENT_DURATION:
- if (!the_event->has_other_event())
- return false;
- *num = the_event->GetAbsTimeToOtherEvent();
- return true;
- case EVENT_COMPLETE_DURATION:
- if (the_event->phase != TRACE_EVENT_PHASE_COMPLETE)
- return false;
- *num = the_event->duration;
- return true;
- case EVENT_PHASE:
- case OTHER_PHASE:
- case PREV_PHASE:
- *num = static_cast<double>(the_event->phase);
- return true;
- case EVENT_HAS_STRING_ARG:
- case OTHER_HAS_STRING_ARG:
- case PREV_HAS_STRING_ARG:
- *num = (the_event->HasStringArg(string_) ? 1.0 : 0.0);
- return true;
- case EVENT_HAS_NUMBER_ARG:
- case OTHER_HAS_NUMBER_ARG:
- case PREV_HAS_NUMBER_ARG:
- *num = (the_event->HasNumberArg(string_) ? 1.0 : 0.0);
- return true;
- case EVENT_ARG:
- case OTHER_ARG:
- case PREV_ARG: {
- // Search for the argument name and return its value if found.
- auto num_i = the_event->arg_numbers.find(string_);
- if (num_i == the_event->arg_numbers.end())
- return false;
- *num = num_i->second;
- return true;
- }
- case EVENT_HAS_OTHER:
- // return 1.0 (true) if the other event exists
- *num = event.other_event ? 1.0 : 0.0;
- return true;
- case EVENT_HAS_PREV:
- *num = event.prev_event ? 1.0 : 0.0;
- return true;
- default:
- return false;
- }
- }
- bool Query::GetMemberValueAsString(const TraceEvent& event,
- std::string* str) const {
- DCHECK_EQ(QUERY_EVENT_MEMBER, type_);
- // This could be a request for a member of |event| or a member of |event|'s
- // associated previous or next event. Store the target event in the_event:
- const TraceEvent* the_event = SelectTargetEvent(&event, member_);
- // Request for member of associated event, but there is no associated event.
- if (!the_event)
- return false;
- switch (member_) {
- case EVENT_CATEGORY:
- case OTHER_CATEGORY:
- case PREV_CATEGORY:
- *str = the_event->category;
- return true;
- case EVENT_NAME:
- case OTHER_NAME:
- case PREV_NAME:
- *str = the_event->name;
- return true;
- case EVENT_ID:
- case OTHER_ID:
- case PREV_ID:
- *str = the_event->id;
- return true;
- case EVENT_ARG:
- case OTHER_ARG:
- case PREV_ARG: {
- // Search for the argument name and return its value if found.
- auto str_i = the_event->arg_strings.find(string_);
- if (str_i == the_event->arg_strings.end())
- return false;
- *str = str_i->second;
- return true;
- }
- default:
- return false;
- }
- }
- Query::Query(const std::string& str)
- : type_(QUERY_STRING),
- operator_(OP_INVALID),
- member_(EVENT_INVALID),
- number_(0),
- string_(str),
- is_pattern_(false) {
- }
- Query::Query(double num)
- : type_(QUERY_NUMBER),
- operator_(OP_INVALID),
- member_(EVENT_INVALID),
- number_(num),
- is_pattern_(false) {
- }
- const Query& Query::left() const {
- return left_->query();
- }
- const Query& Query::right() const {
- return right_->query();
- }
- Query Query::operator==(const Query& rhs) const {
- return Query(*this, rhs, OP_EQ);
- }
- Query Query::operator!=(const Query& rhs) const {
- return Query(*this, rhs, OP_NE);
- }
- Query Query::operator<(const Query& rhs) const {
- return Query(*this, rhs, OP_LT);
- }
- Query Query::operator<=(const Query& rhs) const {
- return Query(*this, rhs, OP_LE);
- }
- Query Query::operator>(const Query& rhs) const {
- return Query(*this, rhs, OP_GT);
- }
- Query Query::operator>=(const Query& rhs) const {
- return Query(*this, rhs, OP_GE);
- }
- Query Query::operator&&(const Query& rhs) const {
- return Query(*this, rhs, OP_AND);
- }
- Query Query::operator||(const Query& rhs) const {
- return Query(*this, rhs, OP_OR);
- }
- Query Query::operator!() const {
- return Query(*this, OP_NOT);
- }
- Query Query::operator+(const Query& rhs) const {
- return Query(*this, rhs, OP_ADD);
- }
- Query Query::operator-(const Query& rhs) const {
- return Query(*this, rhs, OP_SUB);
- }
- Query Query::operator*(const Query& rhs) const {
- return Query(*this, rhs, OP_MUL);
- }
- Query Query::operator/(const Query& rhs) const {
- return Query(*this, rhs, OP_DIV);
- }
- Query Query::operator%(const Query& rhs) const {
- return Query(*this, rhs, OP_MOD);
- }
- Query Query::operator-() const {
- return Query(*this, OP_NEGATE);
- }
- Query::Query(const Query& left, const Query& right, Operator binary_op)
- : operator_(binary_op),
- left_(new QueryNode(left)),
- right_(new QueryNode(right)),
- member_(EVENT_INVALID),
- number_(0) {
- type_ = (binary_op < OP_ADD ?
- QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
- }
- Query::Query(const Query& left, Operator unary_op)
- : operator_(unary_op),
- left_(new QueryNode(left)),
- member_(EVENT_INVALID),
- number_(0) {
- type_ = (unary_op < OP_ADD ?
- QUERY_BOOLEAN_OPERATOR : QUERY_ARITHMETIC_OPERATOR);
- }
- namespace {
- // Search |events| for |query| and add matches to |output|.
- size_t FindMatchingEvents(const std::vector<TraceEvent>& events,
- const Query& query,
- TraceEventVector* output,
- bool ignore_metadata_events) {
- for (const auto& i : events) {
- if (ignore_metadata_events && i.phase == TRACE_EVENT_PHASE_METADATA)
- continue;
- if (query.Evaluate(i))
- output->push_back(&i);
- }
- return output->size();
- }
- bool ParseEventsFromJson(const std::string& json,
- std::vector<TraceEvent>* output) {
- absl::optional<base::Value> root = base::JSONReader::Read(json);
- if (!root)
- return false;
- base::Value::List* list = nullptr;
- if (root->is_list()) {
- list = &root->GetList();
- } else if (root->is_dict()) {
- list = root->GetDict().FindList("traceEvents");
- }
- if (!list)
- return false;
- for (const auto& item : *list) {
- TraceEvent event;
- if (!event.SetFromJSON(&item))
- return false;
- output->push_back(std::move(event));
- }
- return true;
- }
- } // namespace
- // TraceAnalyzer
- TraceAnalyzer::TraceAnalyzer()
- : ignore_metadata_events_(false), allow_association_changes_(true) {}
- TraceAnalyzer::~TraceAnalyzer() = default;
- // static
- std::unique_ptr<TraceAnalyzer> TraceAnalyzer::Create(
- const std::string& json_events) {
- std::unique_ptr<TraceAnalyzer> analyzer(new TraceAnalyzer());
- if (analyzer->SetEvents(json_events))
- return analyzer;
- return nullptr;
- }
- bool TraceAnalyzer::SetEvents(const std::string& json_events) {
- raw_events_.clear();
- if (!ParseEventsFromJson(json_events, &raw_events_))
- return false;
- base::ranges::stable_sort(raw_events_);
- ParseMetadata();
- return true;
- }
- void TraceAnalyzer::AssociateBeginEndEvents() {
- using trace_analyzer::Query;
- Query begin(Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN));
- Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_END));
- Query match(Query::EventName() == Query::OtherName() &&
- Query::EventCategory() == Query::OtherCategory() &&
- Query::EventTid() == Query::OtherTid() &&
- Query::EventPid() == Query::OtherPid());
- AssociateEvents(begin, end, match);
- }
- void TraceAnalyzer::AssociateAsyncBeginEndEvents(bool match_pid) {
- using trace_analyzer::Query;
- Query begin(
- Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
- Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
- Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
- Query end(Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_END) ||
- Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_INTO) ||
- Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_STEP_PAST));
- Query match(Query::EventCategory() == Query::OtherCategory() &&
- Query::EventId() == Query::OtherId());
- if (match_pid) {
- match = match && Query::EventPid() == Query::OtherPid();
- }
- AssociateEvents(begin, end, match);
- }
- void TraceAnalyzer::AssociateEvents(const Query& first,
- const Query& second,
- const Query& match) {
- DCHECK(allow_association_changes_)
- << "AssociateEvents not allowed after FindEvents";
- // Search for matching begin/end event pairs. When a matching end is found,
- // it is associated with the begin event.
- std::vector<TraceEvent*> begin_stack;
- for (auto& this_event : raw_events_) {
- if (second.Evaluate(this_event)) {
- // Search stack for matching begin, starting from end.
- for (int stack_index = static_cast<int>(begin_stack.size()) - 1;
- stack_index >= 0; --stack_index) {
- TraceEvent& begin_event = *begin_stack[stack_index];
- // Temporarily set other to test against the match query.
- const TraceEvent* other_backup = begin_event.other_event;
- begin_event.other_event = &this_event;
- if (match.Evaluate(begin_event)) {
- // Found a matching begin/end pair.
- // Set the associated previous event
- this_event.prev_event = &begin_event;
- // Erase the matching begin event index from the stack.
- begin_stack.erase(begin_stack.begin() + stack_index);
- break;
- }
- // Not a match, restore original other and continue.
- begin_event.other_event = other_backup;
- }
- }
- // Even if this_event is a |second| event that has matched an earlier
- // |first| event, it can still also be a |first| event and be associated
- // with a later |second| event.
- if (first.Evaluate(this_event)) {
- begin_stack.push_back(&this_event);
- }
- }
- }
- void TraceAnalyzer::MergeAssociatedEventArgs() {
- for (auto& i : raw_events_) {
- // Merge all associated events with the first event.
- const TraceEvent* other = i.other_event;
- // Avoid looping by keeping set of encountered TraceEvents.
- std::set<const TraceEvent*> encounters;
- encounters.insert(&i);
- while (other && encounters.find(other) == encounters.end()) {
- encounters.insert(other);
- i.arg_numbers.insert(other->arg_numbers.begin(),
- other->arg_numbers.end());
- i.arg_strings.insert(other->arg_strings.begin(),
- other->arg_strings.end());
- other = other->other_event;
- }
- }
- }
- size_t TraceAnalyzer::FindEvents(const Query& query, TraceEventVector* output) {
- allow_association_changes_ = false;
- output->clear();
- return FindMatchingEvents(
- raw_events_, query, output, ignore_metadata_events_);
- }
- const TraceEvent* TraceAnalyzer::FindFirstOf(const Query& query) {
- TraceEventVector output;
- if (FindEvents(query, &output) > 0)
- return output.front();
- return nullptr;
- }
- const TraceEvent* TraceAnalyzer::FindLastOf(const Query& query) {
- TraceEventVector output;
- if (FindEvents(query, &output) > 0)
- return output.back();
- return nullptr;
- }
- const std::string& TraceAnalyzer::GetThreadName(
- const TraceEvent::ProcessThreadID& thread) {
- // If thread is not found, just add and return empty string.
- return thread_names_[thread];
- }
- void TraceAnalyzer::ParseMetadata() {
- for (const auto& this_event : raw_events_) {
- // Check for thread name metadata.
- if (this_event.phase != TRACE_EVENT_PHASE_METADATA ||
- this_event.name != "thread_name")
- continue;
- std::map<std::string, std::string>::const_iterator string_it =
- this_event.arg_strings.find("name");
- if (string_it != this_event.arg_strings.end())
- thread_names_[this_event.thread] = string_it->second;
- }
- }
- // Utility functions for collecting process-local traces and creating a
- // |TraceAnalyzer| from the result.
- void Start(const std::string& category_filter_string) {
- DCHECK(!base::trace_event::TraceLog::GetInstance()->IsEnabled());
- base::trace_event::TraceLog::GetInstance()->SetEnabled(
- base::trace_event::TraceConfig(category_filter_string, ""),
- base::trace_event::TraceLog::RECORDING_MODE);
- }
- std::unique_ptr<TraceAnalyzer> Stop() {
- DCHECK(base::trace_event::TraceLog::GetInstance()->IsEnabled());
- base::trace_event::TraceLog::GetInstance()->SetDisabled();
- base::trace_event::TraceResultBuffer buffer;
- base::trace_event::TraceResultBuffer::SimpleOutput trace_output;
- buffer.SetOutputCallback(trace_output.GetCallback());
- base::RunLoop run_loop;
- buffer.Start();
- base::trace_event::TraceLog::GetInstance()->Flush(
- base::BindRepeating(&OnTraceDataCollected, run_loop.QuitClosure(),
- base::Unretained(&buffer)));
- run_loop.Run();
- buffer.Finish();
- return TraceAnalyzer::Create(trace_output.json_output);
- }
- // TraceEventVector utility functions.
- bool GetRateStats(const TraceEventVector& events,
- RateStats* stats,
- const RateStatsOptions* options) {
- DCHECK(stats);
- // Need at least 3 events to calculate rate stats.
- const size_t kMinEvents = 3;
- if (events.size() < kMinEvents) {
- LOG(ERROR) << "Not enough events: " << events.size();
- return false;
- }
- std::vector<double> deltas;
- size_t num_deltas = events.size() - 1;
- for (size_t i = 0; i < num_deltas; ++i) {
- double delta = events.at(i + 1)->timestamp - events.at(i)->timestamp;
- if (delta < 0.0) {
- LOG(ERROR) << "Events are out of order";
- return false;
- }
- deltas.push_back(delta);
- }
- base::ranges::sort(deltas);
- if (options) {
- if (options->trim_min + options->trim_max > events.size() - kMinEvents) {
- LOG(ERROR) << "Attempt to trim too many events";
- return false;
- }
- deltas.erase(deltas.begin(), deltas.begin() + options->trim_min);
- deltas.erase(deltas.end() - options->trim_max, deltas.end());
- }
- num_deltas = deltas.size();
- double delta_sum = 0.0;
- for (size_t i = 0; i < num_deltas; ++i)
- delta_sum += deltas[i];
- stats->min_us = *base::ranges::min_element(deltas);
- stats->max_us = *base::ranges::max_element(deltas);
- stats->mean_us = delta_sum / static_cast<double>(num_deltas);
- double sum_mean_offsets_squared = 0.0;
- for (size_t i = 0; i < num_deltas; ++i) {
- double offset = fabs(deltas[i] - stats->mean_us);
- sum_mean_offsets_squared += offset * offset;
- }
- stats->standard_deviation_us =
- sqrt(sum_mean_offsets_squared / static_cast<double>(num_deltas - 1));
- return true;
- }
- bool FindFirstOf(const TraceEventVector& events,
- const Query& query,
- size_t position,
- size_t* return_index) {
- DCHECK(return_index);
- for (size_t i = position; i < events.size(); ++i) {
- if (query.Evaluate(*events[i])) {
- *return_index = i;
- return true;
- }
- }
- return false;
- }
- bool FindLastOf(const TraceEventVector& events,
- const Query& query,
- size_t position,
- size_t* return_index) {
- DCHECK(return_index);
- for (size_t i = std::min(position + 1, events.size()); i != 0; --i) {
- if (query.Evaluate(*events[i - 1])) {
- *return_index = i - 1;
- return true;
- }
- }
- return false;
- }
- bool FindClosest(const TraceEventVector& events,
- const Query& query,
- size_t position,
- size_t* return_closest,
- size_t* return_second_closest) {
- DCHECK(return_closest);
- if (events.empty() || position >= events.size())
- return false;
- size_t closest = events.size();
- size_t second_closest = events.size();
- for (size_t i = 0; i < events.size(); ++i) {
- if (!query.Evaluate(*events.at(i)))
- continue;
- if (closest == events.size()) {
- closest = i;
- continue;
- }
- if (fabs(events.at(i)->timestamp - events.at(position)->timestamp) <
- fabs(events.at(closest)->timestamp - events.at(position)->timestamp)) {
- second_closest = closest;
- closest = i;
- } else if (second_closest == events.size()) {
- second_closest = i;
- }
- }
- if (closest < events.size() &&
- (!return_second_closest || second_closest < events.size())) {
- *return_closest = closest;
- if (return_second_closest)
- *return_second_closest = second_closest;
- return true;
- }
- return false;
- }
- size_t CountMatches(const TraceEventVector& events,
- const Query& query,
- size_t begin_position,
- size_t end_position) {
- if (begin_position >= events.size())
- return 0u;
- end_position = (end_position < events.size()) ? end_position : events.size();
- size_t count = 0u;
- for (size_t i = begin_position; i < end_position; ++i) {
- if (query.Evaluate(*events.at(i)))
- ++count;
- }
- return count;
- }
- } // namespace trace_analyzer
|