arc: Add base tracing model.

This builds base tracing model with ability to query using output of
chrome://tracing.
Tool prototype is available here:
https://drive.google.com/open?id=14LkHgqJNsd19uAwIlgL5x7l0_c6vxG1B

TEST=In context of prototype tool
BUG=b:122555793

Change-Id: Ic67860af53c4652e6cb64e88a13f0b7539220efe
Reviewed-on: https://chromium-review.googlesource.com/c/1406025
Commit-Queue: Yury Khmel <khmel@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#622251}
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index c2d2b8d..0cb6878 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -569,6 +569,12 @@
     "arc/screen_capture/arc_screen_capture_session.h",
     "arc/tracing/arc_tracing_bridge.cc",
     "arc/tracing/arc_tracing_bridge.h",
+    "arc/tracing/arc_tracing_event.cc",
+    "arc/tracing/arc_tracing_event.h",
+    "arc/tracing/arc_tracing_event_matcher.cc",
+    "arc/tracing/arc_tracing_event_matcher.h",
+    "arc/tracing/arc_tracing_model.cc",
+    "arc/tracing/arc_tracing_model.h",
     "arc/tts/arc_tts_service.cc",
     "arc/tts/arc_tts_service.h",
     "arc/user_session/arc_user_session_service.cc",
@@ -2157,6 +2163,7 @@
     "arc/pip/arc_pip_bridge_unittest.cc",
     "arc/policy/arc_policy_bridge_unittest.cc",
     "arc/process/arc_process_unittest.cc",
+    "arc/tracing/arc_tracing_model_unittest.cc",
     "arc/tts/arc_tts_service_unittest.cc",
     "arc/voice_interaction/fake_voice_interaction_controller.cc",
     "arc/voice_interaction/voice_interaction_controller_client_unittest.cc",
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_event.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_event.cc
new file mode 100644
index 0000000..d289a77
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_event.cc
@@ -0,0 +1,268 @@
+// Copyright 2019 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 "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
+
+#include <inttypes.h>
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/common/trace_event_common.h"
+
+namespace arc {
+
+namespace {
+
+constexpr char kKeyArguments[] = "args";
+constexpr char kKeyDuration[] = "dur";
+constexpr char kKeyCategory[] = "cat";
+constexpr char kKeyId[] = "id";
+constexpr char kKeyName[] = "name";
+constexpr char kKeyPid[] = "pid";
+constexpr char kKeyPhase[] = "ph";
+constexpr char kKeyTid[] = "tid";
+constexpr char kKeyTimestamp[] = "ts";
+
+int GetIntegerFromDictionary(const base::DictionaryValue* dictionary,
+                             const std::string& name,
+                             int default_value) {
+  if (!dictionary)
+    return default_value;
+  const base::Value* value =
+      dictionary->FindKeyOfType(name, base::Value::Type::INTEGER);
+  return value ? value->GetInt() : default_value;
+}
+
+double GetDoubleFromDictionary(const base::DictionaryValue* dictionary,
+                               const std::string& name,
+                               double default_value) {
+  if (!dictionary)
+    return default_value;
+  const base::Value* value =
+      dictionary->FindKeyOfType(name, base::Value::Type::DOUBLE);
+  if (value)
+    return value->GetDouble();
+  value = dictionary->FindKeyOfType(name, base::Value::Type::INTEGER);
+  if (value)
+    return value->GetInt();
+  return default_value;
+}
+
+std::string GetStringFromDictionary(const base::DictionaryValue* dictionary,
+                                    const std::string& name,
+                                    const std::string& default_value) {
+  if (!dictionary)
+    return default_value;
+  const base::Value* value =
+      dictionary->FindKeyOfType(name, base::Value::Type::STRING);
+  return value ? value->GetString() : default_value;
+}
+
+}  // namespace
+
+ArcTracingEvent::ArcTracingEvent(std::unique_ptr<base::Value> dictionary)
+    : dictionary_(std::move(dictionary)) {}
+
+ArcTracingEvent::~ArcTracingEvent() = default;
+
+int ArcTracingEvent::GetPid() const {
+  return GetIntegerFromDictionary(GetDictionary(), kKeyPid,
+                                  0 /* default_value */);
+}
+
+void ArcTracingEvent::SetPid(int pid) {
+  dictionary_->SetKey(kKeyPid, base::Value(pid));
+}
+
+int ArcTracingEvent::GetTid() const {
+  return GetIntegerFromDictionary(GetDictionary(), kKeyTid,
+                                  0 /* default_value */);
+}
+
+void ArcTracingEvent::SetTid(int tid) {
+  dictionary_->SetKey(kKeyTid, base::Value(tid));
+}
+
+std::string ArcTracingEvent::GetId() const {
+  return GetStringFromDictionary(GetDictionary(), kKeyId,
+                                 std::string() /* default_value */);
+}
+
+void ArcTracingEvent::SetId(const std::string& id) {
+  dictionary_->SetKey(kKeyId, base::Value(id));
+}
+
+std::string ArcTracingEvent::GetCategory() const {
+  return GetStringFromDictionary(GetDictionary(), kKeyCategory,
+                                 std::string() /* default_value */);
+}
+
+void ArcTracingEvent::SetCategory(const std::string& category) {
+  dictionary_->SetKey(kKeyCategory, base::Value(category));
+}
+
+std::string ArcTracingEvent::GetName() const {
+  return GetStringFromDictionary(GetDictionary(), kKeyName,
+                                 std::string() /* default_value */);
+}
+
+void ArcTracingEvent::SetName(const std::string& name) {
+  dictionary_->SetKey(kKeyName, base::Value(name));
+}
+
+char ArcTracingEvent::GetPhase() const {
+  const std::string ph = GetStringFromDictionary(
+      GetDictionary(), kKeyPhase, std::string() /* default_value */);
+  return ph.length() == 1 ? ph[0] : '\0';
+}
+
+void ArcTracingEvent::SetPhase(char phase) {
+  dictionary_->SetKey(kKeyPhase, base::Value(std::string() + phase));
+}
+
+int64_t ArcTracingEvent::GetTimestamp() const {
+  return GetDoubleFromDictionary(GetDictionary(), kKeyTimestamp,
+                                 0.0 /* default_value */);
+}
+
+void ArcTracingEvent::SetTimestamp(double timestamp) {
+  dictionary_->SetKey(kKeyTimestamp, base::Value(timestamp));
+}
+
+int64_t ArcTracingEvent::GetDuration() const {
+  return GetDoubleFromDictionary(GetDictionary(), kKeyDuration,
+                                 0.0 /* default_value */);
+}
+
+void ArcTracingEvent::SetDuration(double duration) {
+  dictionary_->SetKey(kKeyDuration, base::Value(duration));
+}
+
+int64_t ArcTracingEvent::GetEndTimestamp() const {
+  return GetTimestamp() + GetDuration();
+}
+
+const base::DictionaryValue* ArcTracingEvent::GetDictionary() const {
+  const base::DictionaryValue* dictionary = nullptr;
+  dictionary_->GetAsDictionary(&dictionary);
+  return dictionary;
+}
+
+const base::DictionaryValue* ArcTracingEvent::GetArgs() const {
+  const base::Value* value =
+      dictionary_->FindKeyOfType(kKeyArguments, base::Value::Type::DICTIONARY);
+  if (!value)
+    return nullptr;
+  const base::DictionaryValue* args = nullptr;
+  value->GetAsDictionary(&args);
+  return args;
+}
+
+std::string ArcTracingEvent::GetArgAsString(
+    const std::string& name,
+    const std::string& default_value) const {
+  return GetStringFromDictionary(GetArgs(), name, default_value);
+}
+
+int ArcTracingEvent::GetArgAsInteger(const std::string& name,
+                                     int default_value) const {
+  return GetIntegerFromDictionary(GetArgs(), name, default_value);
+}
+
+double ArcTracingEvent::GetArgAsDouble(const std::string& name,
+                                       double default_value) const {
+  return GetDoubleFromDictionary(GetArgs(), name, default_value);
+}
+
+ArcTracingEvent::Position ArcTracingEvent::ClassifyPositionOf(
+    const ArcTracingEvent& other) const {
+  const int64_t this_start = GetTimestamp();
+  const int64_t this_end = this_start + GetDuration();
+  const int64_t other_start = other.GetTimestamp();
+  const int64_t other_end = other_start + other.GetDuration();
+  if (this_start <= other_start && this_end >= other_end)
+    return Position::kInside;
+  if (this_end <= other_start)
+    return Position::kAfter;
+  if (other_end <= this_start)
+    return Position::kBefore;
+  return Position::kOverlap;
+}
+
+bool ArcTracingEvent::AppendChild(std::unique_ptr<ArcTracingEvent> child) {
+  if (ClassifyPositionOf(*child) != Position::kInside)
+    return false;
+
+  if (children_.empty() ||
+      children_.back()->ClassifyPositionOf(*child) == Position::kAfter) {
+    children_.push_back(std::move(child));
+    return true;
+  }
+  return children_.back()->AppendChild(std::move(child));
+}
+
+bool ArcTracingEvent::Validate() const {
+  if (!GetPid() || GetCategory().empty() || GetName().empty())
+    return false;
+
+  switch (GetPhase()) {
+    case TRACE_EVENT_PHASE_COMPLETE:
+    case TRACE_EVENT_PHASE_COUNTER:
+      if (!GetTid())
+        return false;
+      break;
+    case TRACE_EVENT_PHASE_METADATA:
+      break;
+    case TRACE_EVENT_PHASE_ASYNC_BEGIN:
+    case TRACE_EVENT_PHASE_ASYNC_END:
+    case TRACE_EVENT_PHASE_ASYNC_STEP_INTO:
+      if (!GetTid() || GetId().empty())
+        return false;
+      break;
+    default:
+      return false;
+  }
+  return true;
+}
+
+std::string ArcTracingEvent::ToString() const {
+  std::string result = base::StringPrintf(
+      "%d|%d|%" PRId64 "|%" PRId64 "|%c|%s|%s|%s", GetPid(), GetTid(),
+      GetTimestamp(), GetDuration(), GetPhase(), GetCategory().c_str(),
+      GetName().c_str(), GetId().c_str());
+  const base::DictionaryValue* args = GetArgs();
+  if (args) {
+    bool first_arg = true;
+    for (const auto& arg : *args) {
+      if (first_arg) {
+        result += "|";
+        first_arg = false;
+      } else {
+        result += ",";
+      }
+      int int_value;
+      double double_value;
+      std::string string_value;
+      if (arg.second->GetAsString(&string_value)) {
+        result += base::StringPrintf("%s=%s", arg.first.c_str(),
+                                     string_value.c_str());
+      } else if (arg.second->GetAsInteger(&int_value)) {
+        result += base::StringPrintf("%s=%i", arg.first.c_str(), int_value);
+      } else if (arg.second->GetAsDouble(&double_value)) {
+        result += base::StringPrintf("%s=%f", arg.first.c_str(), double_value);
+      } else {
+        result += base::StringPrintf("%s=?", arg.first.c_str());
+      }
+    }
+  }
+  return result;
+}
+
+void ArcTracingEvent::Dump(const std::string& prefix,
+                           std::ostream& stream) const {
+  stream << prefix << ToString() << "\n";
+  for (const auto& event : children_)
+    event->Dump(prefix + "  ", stream);
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_event.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_event.h
new file mode 100644
index 0000000..9772dd94
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_event.h
@@ -0,0 +1,127 @@
+// Copyright 2019 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/values.h"
+
+namespace arc {
+
+// |ArcTracingEvent| is a wrapper over |base::DictionaryValue| that is used to
+// represent trace event in Chrome. |ArcTracingEvent| is hierarchical and
+// can contain children. Setter methods are used to convert system trace events
+// that are not dictionary based to the common Chrome format.
+class ArcTracingEvent {
+ public:
+  enum class Position {
+    kBefore,   // event is before the compared event
+    kInside,   // event is inside the compared event.
+    kAfter,    // event is after the compared event.
+    kOverlap,  // event overlaps with compared event.
+  };
+
+  explicit ArcTracingEvent(std::unique_ptr<base::Value> dictionary);
+  ~ArcTracingEvent();
+
+  // Gets process id of the event. Returns 0 if not set.
+  int GetPid() const;
+  // Sets process id of the event.
+  void SetPid(int pid);
+
+  // Gets thread id of the event. Returns 0 if not set.
+  int GetTid() const;
+  // Sets thread id of the event.
+  void SetTid(int tid);
+
+  // Gets id of the group of events. Returns empty string if not set.
+  std::string GetId() const;
+  // Sets id of the event.
+  void SetId(const std::string& id);
+
+  // Gets category of the event. Returns empty string if not set.
+  std::string GetCategory() const;
+  // Sets category of the event.
+  void SetCategory(const std::string& category);
+
+  // Gets name of the event. Returns empty string if not set.
+  std::string GetName() const;
+  // Sets name of the event.
+  void SetName(const std::string& name);
+
+  // Gets phase of the event. Returns 0 if not set.
+  char GetPhase() const;
+  // Sets phase of the event.
+  void SetPhase(char phase);
+
+  // Gets timestamp of the start of the event. Return 0 if not set.
+  int64_t GetTimestamp() const;
+  // Sets timestamp of the start of the event.
+  void SetTimestamp(double timestamp);
+
+  // Gets duration of the event.  Return 0 if not set. It is optional for some
+  // events.
+  int64_t GetDuration() const;
+  // Sets duration of the event.
+  void SetDuration(double duration);
+
+  // Gets timestamp of the end of the event.
+  int64_t GetEndTimestamp() const;
+
+  // Returns base representation of the event as a |base::DictionaryValue|.
+  const base::DictionaryValue* GetDictionary() const;
+
+  // Returns set of arguments as a |base::DictionaryValue|.
+  const base::DictionaryValue* GetArgs() const;
+
+  // Gets argument as string. Return |default_value| if not found.
+  std::string GetArgAsString(const std::string& name,
+                             const std::string& default_value) const;
+
+  // Gets argument as integer. Returns |default_value| if not found.
+  int GetArgAsInteger(const std::string& name, int default_value) const;
+
+  // Gets argument as double. Returns |default_value| if not found.
+  double GetArgAsDouble(const std::string& name, double default_value) const;
+
+  // Classifies the position of another event relative to the current event.
+  Position ClassifyPositionOf(const ArcTracingEvent& other) const;
+
+  // Recursively adds child trace event. If child event is not inside the
+  // current event than child event is not added and false is returned.
+  // Based on building constraints, child element can be appended to the end
+  // of the list of child events or to the last child event.
+  bool AppendChild(std::unique_ptr<ArcTracingEvent> child);
+
+  // Validates that event contains correct information.
+  bool Validate() const;
+
+  // Returns string representation of this event.
+  std::string ToString() const;
+
+  // Dumps this event and its children to |stream|. |prefix| is used for
+  // formatting.
+  void Dump(const std::string& prefix, std::ostream& stream) const;
+
+  const std::vector<std::unique_ptr<ArcTracingEvent>>& children() const {
+    return children_;
+  }
+
+ private:
+  std::vector<std::unique_ptr<ArcTracingEvent>> children_;
+  std::unique_ptr<base::Value> dictionary_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingEvent);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_H_
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.cc
new file mode 100644
index 0000000..140f78f
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.cc
@@ -0,0 +1,74 @@
+// Copyright 2019 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 "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h"
+
+#include "base/strings/string_split.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
+
+namespace arc {
+
+ArcTracingEventMatcher::ArcTracingEventMatcher() = default;
+
+ArcTracingEventMatcher::ArcTracingEventMatcher(const std::string& data) {
+  std::string::size_type position = data.find(':');
+  DCHECK(position);
+  category_ = data.substr(0, position);
+  name_ = data.substr(position + 1);
+  DCHECK(!category_.empty() && !name_.empty());
+  position = name_.find('(');
+  if (position == std::string::npos)
+    return;
+
+  DCHECK_EQ(')', name_.back());
+  for (const std::string& arg : base::SplitString(
+           name_.substr(position + 1, name_.length() - position - 2), ";",
+           base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+    std::string::size_type separator = arg.find('=');
+    args_[arg.substr(0, separator)] = arg.substr(separator + 1);
+  }
+  name_ = name_.substr(0, position);
+}
+
+ArcTracingEventMatcher& ArcTracingEventMatcher::SetPhase(char phase) {
+  phase_ = phase;
+  return *this;
+}
+
+ArcTracingEventMatcher& ArcTracingEventMatcher::SetCategory(
+    const std::string& category) {
+  category_ = category;
+  return *this;
+}
+
+ArcTracingEventMatcher& ArcTracingEventMatcher::SetName(
+    const std::string& name) {
+  name_ = name;
+  return *this;
+}
+
+ArcTracingEventMatcher& ArcTracingEventMatcher::AddArgument(
+    const std::string& key,
+    const std::string& value) {
+  args_[key] = value;
+  return *this;
+}
+
+bool ArcTracingEventMatcher::Match(const ArcTracingEvent& event) const {
+  if (phase_ && phase_ != event.GetPhase())
+    return false;
+  if (!category_.empty() && event.GetCategory() != category_)
+    return false;
+  if (!name_.empty() && event.GetName() != name_)
+    return false;
+  for (const auto& arg : args_) {
+    if (event.GetArgAsString(arg.first, std::string() /* default_value */) !=
+        arg.second) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h
new file mode 100644
index 0000000..da2795a7
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h
@@ -0,0 +1,61 @@
+// Copyright 2019 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_MATCHER_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_MATCHER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+
+namespace arc {
+
+class ArcTracingEvent;
+
+// Helper that allows to match events based on provided criteria.
+class ArcTracingEventMatcher {
+ public:
+  ArcTracingEventMatcher();
+  // Format category:name(arg_name=arg_value;..) For example:
+  // exo:Surface::Attach
+  // exo:Surface::Attach(buffer_id=0x7f9f5110690)
+  explicit ArcTracingEventMatcher(const std::string& data);
+
+  // Returns true in case |event| matches criteria set.
+  bool Match(const ArcTracingEvent& event) const;
+
+  // Sets the expected phase. Tested event does not match if its phase does not
+  // match |phase|. This is an optional criteria.
+  ArcTracingEventMatcher& SetPhase(char phase);
+  // Sets the expected category. Tested event does not match if its category
+  // does not match |category|. This is an optional criteria.
+  ArcTracingEventMatcher& SetCategory(const std::string& category);
+  // Sets the expected name. Tested event does not match if its name does not
+  // match |name|. This is an optional criteria.
+  ArcTracingEventMatcher& SetName(const std::string& name);
+  // Adds the expected argument. Tested event does not match if it does not
+  // contains the argument specified by |key| or argument does not match
+  // |value|.
+  ArcTracingEventMatcher& AddArgument(const std::string& key,
+                                      const std::string& value);
+
+ private:
+  // Defines the phase to match.
+  char phase_ = 0;
+  // Defines the category to match.
+  std::string category_;
+  // Defines the name to match.
+  std::string name_;
+  // Defines set of arguments to match if needed.
+  std::map<std::string, std::string> args_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingEventMatcher);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_EVENT_MATCHER_H_
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
new file mode 100644
index 0000000..397e498
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.cc
@@ -0,0 +1,389 @@
+// Copyright 2019 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 "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h"
+
+#include "base/json/json_reader.h"
+#include "base/strings/string_split.h"
+#include "base/trace_event/common/trace_event_common.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h"
+
+namespace arc {
+
+namespace {
+
+constexpr char kAndroidCategory[] = "android";
+constexpr char kTracingMarkWrite[] = ": tracing_mark_write: ";
+constexpr int kTracingMarkWriteLength = sizeof(kTracingMarkWrite) - 1;
+constexpr char kTraceEventClockSync[] = "trace_event_clock_sync: ";
+constexpr int kTraceEventClockSyncLength = sizeof(kTraceEventClockSync) - 1;
+
+// Helper function that converts a portion of string to uint32_t value. |pos|
+// specifies the position in string to parse. |end_char| specifies expected
+// character at the end. \0 means parse to the end. Returns the position of
+// the next character after parsed digits or std::string::npos in case parsing
+// failed. This is performance oriented and main idea is to avoid extra memory
+// allocations due to sub-string extractions.
+size_t ParseUint32(const std::string& str,
+                   size_t pos,
+                   char end_char,
+                   uint32_t* res) {
+  *res = 0;
+  const size_t len = str.length();
+  while (true) {
+    const char& c = str[pos];
+    if (c != ' ')
+      break;
+    if (++pos == len)
+      return std::string::npos;
+  }
+
+  while (true) {
+    const char& c = str[pos];
+    if (c < '0' || c > '9')
+      return std::string::npos;
+    const uint32_t prev = *res;
+    *res = *res * 10 + c - '0';
+    if (prev != (*res - c + '0') / 10)
+      return std::string::npos;  // overflow
+    if (++pos == len || str[pos] == end_char)
+      break;
+  }
+
+  return pos;
+}
+
+std::vector<std::unique_ptr<ArcTracingEventMatcher>> BuildSelector(
+    const std::string& query) {
+  std::vector<std::unique_ptr<ArcTracingEventMatcher>> result;
+  for (const std::string& segment : base::SplitString(
+           query, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+    result.emplace_back(std::make_unique<ArcTracingEventMatcher>(segment));
+  }
+  return result;
+}
+
+void SelectRecursively(
+    size_t level,
+    const ArcTracingEvent* event,
+    const std::vector<std::unique_ptr<ArcTracingEventMatcher>>& selector,
+    ArcTracingModel::TracingEventPtrs* collector) {
+  if (level >= selector.size())
+    return;
+  if (!selector[level]->Match(*event))
+    return;
+  if (level == selector.size() - 1) {
+    // Last segment
+    collector->push_back(event);
+  } else {
+    for (const auto& child : event->children())
+      SelectRecursively(level + 1, child.get(), selector, collector);
+  }
+}
+
+}  // namespace
+
+ArcTracingModel::ArcTracingModel() = default;
+
+ArcTracingModel::~ArcTracingModel() = default;
+
+bool ArcTracingModel::Build(const std::string& data) {
+  std::unique_ptr<base::Value> value = base::JSONReader::Read(data);
+  if (!value) {
+    LOG(ERROR) << "Cannot parse trace data";
+    return false;
+  }
+
+  base::DictionaryValue* dictionary;
+  if (!value->GetAsDictionary(&dictionary)) {
+    LOG(ERROR) << "Trace data is not dictionary";
+    return false;
+  }
+
+  std::string sys_traces;
+  if (dictionary->GetString("systemTraceEvents", &sys_traces)) {
+    if (!ConvertSysTraces(sys_traces)) {
+      LOG(ERROR) << "Failed to convert systrace data";
+      return false;
+    }
+  }
+
+  base::ListValue* events;
+  if (!dictionary->GetList("traceEvents", &events)) {
+    LOG(ERROR) << "No trace events";
+    return false;
+  }
+
+  if (!ProcessEvent(events)) {
+    LOG(ERROR) << "No trace events";
+    return false;
+  }
+
+  return true;
+}
+
+ArcTracingModel::TracingEventPtrs ArcTracingModel::Select(
+    const std::string query) const {
+  ArcTracingModel::TracingEventPtrs collector;
+  const std::vector<std::unique_ptr<ArcTracingEventMatcher>> selector =
+      BuildSelector(query);
+  for (const auto& thread_events : per_thread_events_) {
+    for (const auto& root : thread_events.second)
+      SelectRecursively(0, root.get(), selector, &collector);
+  }
+  for (const auto& group_events : group_events_) {
+    for (const auto& root : group_events.second)
+      SelectRecursively(0, root.get(), selector, &collector);
+  }
+  return collector;
+}
+
+ArcTracingModel::TracingEventPtrs ArcTracingModel::Select(
+    const ArcTracingEvent* event,
+    const std::string query) const {
+  ArcTracingModel::TracingEventPtrs collector;
+  for (const auto& child : event->children())
+    SelectRecursively(0, child.get(), BuildSelector(query), &collector);
+  return collector;
+}
+
+bool ArcTracingModel::ProcessEvent(base::ListValue* events) {
+  base::ListValue::iterator it = events->begin();
+  while (it != events->end()) {
+    std::unique_ptr<base::Value> event_data;
+    // Take ownership.
+    it = events->Erase(it, &event_data);
+    if (!event_data->is_dict()) {
+      LOG(ERROR) << "Event is not a dictionary";
+      return false;
+    }
+
+    std::unique_ptr<ArcTracingEvent> event =
+        std::make_unique<ArcTracingEvent>(std::move(event_data));
+    switch (event->GetPhase()) {
+      case TRACE_EVENT_PHASE_METADATA:
+      case TRACE_EVENT_PHASE_COMPLETE:
+      case TRACE_EVENT_PHASE_COUNTER:
+      case TRACE_EVENT_PHASE_ASYNC_BEGIN:
+      case TRACE_EVENT_PHASE_ASYNC_STEP_INTO:
+      case TRACE_EVENT_PHASE_ASYNC_END:
+        break;
+      default:
+        // Ignore at this moment. They are not currently used.
+        continue;
+    }
+
+    if (!event->Validate()) {
+      LOG(ERROR) << "Invalid event found " << event->ToString();
+      return false;
+    }
+
+    switch (event->GetPhase()) {
+      case TRACE_EVENT_PHASE_METADATA:
+        metadata_events_.push_back(std::move(event));
+        break;
+      case TRACE_EVENT_PHASE_ASYNC_BEGIN:
+      case TRACE_EVENT_PHASE_ASYNC_STEP_INTO:
+      case TRACE_EVENT_PHASE_ASYNC_END:
+        group_events_[event->GetId()].push_back(std::move(event));
+        break;
+      case TRACE_EVENT_PHASE_COMPLETE:
+      case TRACE_EVENT_PHASE_COUNTER:
+        if (!AddToThread(std::move(event))) {
+          LOG(ERROR) << "Cannot add event to threads " << event->ToString();
+          return false;
+        }
+        break;
+      default:
+        NOTREACHED();
+        return false;
+    }
+  }
+
+  // Sort group events based on timestamp. They are asynchronous and may come in
+  // random order.
+  for (auto& gr : group_events_) {
+    std::sort(gr.second.begin(), gr.second.end(),
+              [](std::unique_ptr<ArcTracingEvent>& a,
+                 std::unique_ptr<ArcTracingEvent>& b) {
+                return a->GetTimestamp() < b->GetTimestamp();
+              });
+  }
+
+  return true;
+}
+
+bool ArcTracingModel::ConvertSysTraces(const std::string& sys_traces) {
+  size_t new_line_pos = 0;
+  // To keep in correct order of creation. This converts pair of 'B' and 'E'
+  // events to the completed event, 'X'.
+  TracingEvents converted_events;
+  std::map<uint32_t, std::vector<ArcTracingEvent*>>
+      per_thread_pending_events_stack;
+  while (true) {
+    // Get end of line.
+    size_t end_line_pos = sys_traces.find('\n', new_line_pos);
+    if (end_line_pos == std::string::npos)
+      break;
+
+    const std::string line =
+        sys_traces.substr(new_line_pos, end_line_pos - new_line_pos);
+    new_line_pos = end_line_pos + 1;
+
+    // Skip comments and empty lines
+    if (line.empty() || line[0] == '#')
+      continue;
+
+    // Trace event has following format.
+    //            TASK-PID   CPU#  ||||    TIMESTAMP  FUNCTION
+    //               | |       |   ||||       |         |
+    // Until TIMESTAMP we have fixed position for elements.
+    if (line.length() < 35 || line[16] != '-' || line[22] != ' ' ||
+        line[28] != ' ' || line[33] != ' ') {
+      LOG(ERROR) << "Cannot recognize trace event: " << line;
+      return false;
+    }
+
+    uint32_t tid;
+    if (ParseUint32(line, 17, ' ', &tid) == std::string::npos) {
+      LOG(ERROR) << "Cannot parse tid in trace event: " << line;
+      return false;
+    }
+    uint32_t timestamp_high;
+    uint32_t timestamp_low;
+    const size_t pos_dot = ParseUint32(line, 34, '.', &timestamp_high);
+    if (pos_dot == std::string::npos) {
+      LOG(ERROR) << "Cannot parse timestamp in trace event: " << line;
+      return false;
+    }
+    const size_t separator_position =
+        ParseUint32(line, pos_dot + 1, ':', &timestamp_low);
+    if (separator_position == std::string::npos) {
+      LOG(ERROR) << "Cannot parse timestamp in trace event: " << line;
+      return false;
+    }
+
+    const size_t event_position = separator_position + kTracingMarkWriteLength;
+    // Skip not relevant information. We only have interest what was written
+    // from Android using tracing marker.
+    if (event_position + 2 >= line.length() ||
+        strncmp(&line[separator_position], kTracingMarkWrite,
+                kTracingMarkWriteLength)) {
+      continue;
+    }
+
+    if (event_position + kTraceEventClockSyncLength < line.length() &&
+        !strncmp(&line[event_position], kTraceEventClockSync,
+                 kTraceEventClockSyncLength)) {
+      // Ignore this service message.
+      continue;
+    }
+
+    if (line[event_position + 1] != '|') {
+      LOG(ERROR) << "Cannot recognize trace marker event: " << line;
+      return false;
+    }
+
+    const char phase = line[event_position];
+    const double timestamp = 1000000L * timestamp_high + timestamp_low;
+
+    uint32_t pid;
+    switch (phase) {
+      case TRACE_EVENT_PHASE_BEGIN:
+      case TRACE_EVENT_PHASE_COUNTER: {
+        const size_t name_pos =
+            ParseUint32(line, event_position + 2, '|', &pid);
+        if (name_pos == std::string::npos) {
+          LOG(ERROR) << "Cannot parse pid of trace event: " << line;
+          return false;
+        }
+        const std::string name = line.substr(name_pos + 1);
+        std::unique_ptr<ArcTracingEvent> event =
+            std::make_unique<ArcTracingEvent>(
+                std::make_unique<base::DictionaryValue>());
+        event->SetPid(pid);
+        event->SetTid(tid);
+        event->SetTimestamp(timestamp);
+        event->SetCategory(kAndroidCategory);
+        event->SetName(name);
+        if (phase == TRACE_EVENT_PHASE_BEGIN)
+          per_thread_pending_events_stack[tid].push_back(event.get());
+        else
+          event->SetPhase(TRACE_EVENT_PHASE_COUNTER);
+        converted_events.push_back(std::move(event));
+      } break;
+      case TRACE_EVENT_PHASE_END: {
+        // Beginning event may not exist.
+        if (per_thread_pending_events_stack[tid].empty())
+          continue;
+        if (ParseUint32(line, event_position + 2, '\0', &pid) ==
+            std::string::npos) {
+          LOG(ERROR) << "Cannot parse pid of trace event: " << line;
+          return false;
+        }
+        ArcTracingEvent* completed_event =
+            per_thread_pending_events_stack[tid].back();
+        per_thread_pending_events_stack[tid].pop_back();
+        completed_event->SetPhase(TRACE_EVENT_PHASE_COMPLETE);
+        completed_event->SetDuration(timestamp -
+                                     completed_event->GetTimestamp());
+      } break;
+      default:
+        LOG(ERROR) << "Unsupported type of trace event: " << line;
+        return false;
+    }
+  }
+  // Close all pending tracing event, assuming last event is 0 duration.
+  for (auto& pending_events : per_thread_pending_events_stack) {
+    if (pending_events.second.empty())
+      continue;
+    const double last_timestamp = pending_events.second.back()->GetTimestamp();
+    for (ArcTracingEvent* pending_event : pending_events.second) {
+      pending_event->SetDuration(last_timestamp -
+                                 pending_event->GetTimestamp());
+      pending_event->SetPhase(TRACE_EVENT_PHASE_COMPLETE);
+    }
+  }
+
+  // Now put events to the thread models.
+  for (auto& converted_event : converted_events) {
+    if (!AddToThread(std::move(converted_event))) {
+      LOG(ERROR) << "Cannot add systrace event to threads "
+                 << converted_event->ToString();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool ArcTracingModel::AddToThread(std::unique_ptr<ArcTracingEvent> event) {
+  const uint64_t full_id = event->GetPid() * 0x100000000L + event->GetTid();
+  std::vector<std::unique_ptr<ArcTracingEvent>>& thread_roots =
+      per_thread_events_[full_id];
+  if (thread_roots.empty() || thread_roots.back()->ClassifyPositionOf(*event) ==
+                                  ArcTracingEvent::Position::kAfter) {
+    // First event for the thread or event is after already existing last root
+    // event. Add as a new root.
+    thread_roots.push_back(std::move(event));
+    return true;
+  }
+
+  return thread_roots.back()->AppendChild(std::move(event));
+}
+
+void ArcTracingModel::Dump(std::ostream& stream) const {
+  for (auto& gr : group_events_) {
+    for (const auto& event : gr.second)
+      event->Dump("", stream);
+  }
+
+  for (const auto& gr : per_thread_events_) {
+    for (const auto& event : gr.second)
+      event->Dump("", stream);
+  }
+}
+
+}  // namespace arc
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
new file mode 100644
index 0000000..7d14274
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model.h
@@ -0,0 +1,77 @@
+// Copyright 2019 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_MODEL_H_
+#define CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_MODEL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/values.h"
+
+namespace arc {
+
+class ArcTracingEvent;
+
+// This is a base model that is built from the output of Chrome tracing
+// (chrome://tracing). It contains native Chrome test events and system kernel
+// events converted to common Chrome test events. Events are kept by thread or
+// by group in case of asynchronous events. Events are hierarchical and each
+// thread or group is represented by one top-level event.
+// There are methods to query the model for particular events.
+// |ArcTracingModel| is usually used as a source for more specialized models.
+class ArcTracingModel {
+ public:
+  using TracingEvents = std::vector<std::unique_ptr<ArcTracingEvent>>;
+  using TracingEventPtrs = std::vector<const ArcTracingEvent*>;
+
+  ArcTracingModel();
+  ~ArcTracingModel();
+
+  // Builds model from string data in Json format. Returns false if model
+  // cannot be built.
+  bool Build(const std::string& data);
+
+  // Selects list of events according to |query|. |query| consists from segments
+  // separated by '/' where segment is in format
+  // category:name(arg_name=arg_value;..). See ArcTracingEventMatcher for more
+  // details. Processing starts from the each root node for thread or group.
+  TracingEventPtrs Select(const std::string query) const;
+  // Similar to case above but starts from provided event |event|.
+  TracingEventPtrs Select(const ArcTracingEvent* event,
+                          const std::string query) const;
+
+  // Dumps this model to |stream|.
+  void Dump(std::ostream& stream) const;
+
+ private:
+  // Processes list of events. Returns true in case all events were processed
+  // successfully.
+  bool ProcessEvent(base::ListValue* events);
+
+  // Converts sys traces events to the |base::Dictionary| based format used in
+  // Chrome.
+  bool ConvertSysTraces(const std::string& sys_traces);
+
+  // Adds tracing event to the thread model hierarchy.
+  bool AddToThread(std::unique_ptr<ArcTracingEvent> event);
+
+  // Contains events separated by threads. Key is a composition of pid and tid.
+  std::map<uint64_t, TracingEvents> per_thread_events_;
+  // Contains events, separated by id of the event. Used for asynchronous
+  // tracing events.
+  std::map<std::string, TracingEvents> group_events_;
+
+  // Metadata events.
+  TracingEvents metadata_events_;
+
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingModel);
+};
+
+}  // namespace arc
+
+#endif  // CHROME_BROWSER_CHROMEOS_ARC_TRACING_ARC_TRACING_MODEL_H_
diff --git a/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
new file mode 100644
index 0000000..4cab497
--- /dev/null
+++ b/chrome/browser/chromeos/arc/tracing/arc_tracing_model_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2019 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 "chrome/browser/chromeos/arc/tracing/arc_tracing_model.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
+#include "base/macros.h"
+#include "base/path_service.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_event.h"
+#include "chrome/browser/chromeos/arc/tracing/arc_tracing_event_matcher.h"
+#include "chrome/common/chrome_paths.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/zlib/google/compression_utils.h"
+
+namespace arc {
+
+namespace {
+
+constexpr char kAcquireBufferQuery[] =
+    "android:onMessageReceived/android:handleMessageInvalidate/"
+    "android:latchBuffer/android:updateTexImage/android:acquireBuffer";
+
+constexpr char kAttachSurfaceQueury[] =
+    "toplevel:OnLibevent/exo:Surface::Attach";
+
+constexpr char kTestEvent[] =
+    R"({"pid":4640,"tid":4641,"id":"1234","ts":14241877057,"ph":"X","cat":"exo",
+        "name":"Surface::Attach",
+        "args":{"buffer_id":"0x7f9f5110690","app_id":"org.chromium.arc.6"},
+        "dur":10,"tdur":9,"tts":1216670360})";
+
+constexpr char kAppId[] = "app_id";
+constexpr char kAppIdValue[] = "org.chromium.arc.6";
+constexpr char kBufferId[] = "buffer_id";
+constexpr char kBufferIdBad[] = "_buffer_id";
+constexpr char kBufferIdValue[] = "0x7f9f5110690";
+constexpr char kBufferIdValueBad[] = "_0x7f9f5110690";
+constexpr char kDefault[] = "default";
+constexpr char kExo[] = "exo";
+constexpr char kExoBad[] = "_exo";
+constexpr char kPhaseX = 'X';
+constexpr char kPhaseP = 'P';
+constexpr char kSurfaceAttach[] = "Surface::Attach";
+constexpr char kSurfaceAttachBad[] = "_Surface::Attach";
+
+}  // namespace
+
+class ArcTracingModelTest : public testing::Test {
+ public:
+  ArcTracingModelTest() = default;
+  ~ArcTracingModelTest() override = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcTracingModelTest);
+};
+
+// TopLevel test is performed on the data collected from the real test device.
+// It tests that model can be built from the provided data and queries work.
+TEST_F(ArcTracingModelTest, TopLevel) {
+  base::FilePath base_path;
+  base::PathService::Get(chrome::DIR_TEST_DATA, &base_path);
+  const base::FilePath tracing_path =
+      base_path.Append("arc_graphics_tracing").Append("trace.dat.gz");
+
+  std::string tracing_data_compressed;
+  ASSERT_TRUE(base::ReadFileToString(tracing_path, &tracing_data_compressed));
+
+  std::string tracing_data;
+  ASSERT_TRUE(
+      compression::GzipUncompress(tracing_data_compressed, &tracing_data));
+
+  ArcTracingModel model;
+  ASSERT_TRUE(model.Build(tracing_data));
+
+  // Perform several well-known queries.
+  EXPECT_FALSE(model.Select(kAcquireBufferQuery).empty());
+  EXPECT_FALSE(model.Select(kAttachSurfaceQueury).empty());
+
+  std::stringstream ss;
+  model.Dump(ss);
+  EXPECT_FALSE(ss.str().empty());
+}
+
+TEST_F(ArcTracingModelTest, Event) {
+  const ArcTracingEvent event(base::JSONReader::Read(kTestEvent));
+
+  EXPECT_EQ(4640, event.GetPid());
+  EXPECT_EQ(4641, event.GetTid());
+  EXPECT_EQ("1234", event.GetId());
+  EXPECT_EQ(kExo, event.GetCategory());
+  EXPECT_EQ(kSurfaceAttach, event.GetName());
+  EXPECT_EQ(kPhaseX, event.GetPhase());
+  EXPECT_EQ(14241877057L, event.GetTimestamp());
+  EXPECT_EQ(10, event.GetDuration());
+  EXPECT_EQ(14241877067L, event.GetEndTimestamp());
+  EXPECT_NE(nullptr, event.GetDictionary());
+  EXPECT_EQ(kBufferIdValue, event.GetArgAsString(kBufferId, std::string()));
+  EXPECT_EQ(kDefault, event.GetArgAsString(kBufferIdBad, kDefault));
+}
+
+TEST_F(ArcTracingModelTest, EventClassification) {
+  const ArcTracingEvent event(base::JSONReader::Read(kTestEvent));
+
+  ArcTracingEvent event_before(base::JSONReader::Read(kTestEvent));
+  event_before.SetTimestamp(event.GetTimestamp() - event.GetDuration());
+  EXPECT_EQ(ArcTracingEvent::Position::kBefore,
+            event.ClassifyPositionOf(event_before));
+
+  ArcTracingEvent event_after(base::JSONReader::Read(kTestEvent));
+  event_after.SetTimestamp(event.GetTimestamp() + event.GetDuration());
+  EXPECT_EQ(ArcTracingEvent::Position::kAfter,
+            event.ClassifyPositionOf(event_after));
+
+  ArcTracingEvent event_inside(base::JSONReader::Read(kTestEvent));
+  event_inside.SetTimestamp(event.GetTimestamp() + 1);
+  event_inside.SetDuration(event.GetDuration() - 2);
+  EXPECT_EQ(ArcTracingEvent::Position::kInside,
+            event.ClassifyPositionOf(event_inside));
+  EXPECT_EQ(ArcTracingEvent::Position::kInside,
+            event.ClassifyPositionOf(event));
+
+  ArcTracingEvent event_overlap(base::JSONReader::Read(kTestEvent));
+  event_overlap.SetTimestamp(event.GetTimestamp() + 1);
+  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
+            event.ClassifyPositionOf(event_overlap));
+  event_overlap.SetTimestamp(event.GetTimestamp());
+  event_overlap.SetDuration(event.GetDuration() + 1);
+  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
+            event.ClassifyPositionOf(event_overlap));
+  event_overlap.SetTimestamp(event.GetTimestamp() - 1);
+  event_overlap.SetDuration(event.GetDuration() + 2);
+  EXPECT_EQ(ArcTracingEvent::Position::kOverlap,
+            event.ClassifyPositionOf(event_overlap));
+}
+
+TEST_F(ArcTracingModelTest, EventAppendChild) {
+  ArcTracingEvent event(base::JSONReader::Read(kTestEvent));
+
+  // Impossible to append the even that is bigger than target.
+  std::unique_ptr<ArcTracingEvent> event_overlap =
+      std::make_unique<ArcTracingEvent>(base::JSONReader::Read(kTestEvent));
+  event_overlap->SetTimestamp(event.GetTimestamp() + 1);
+  EXPECT_FALSE(event.AppendChild(std::move(event_overlap)));
+
+  std::unique_ptr<ArcTracingEvent> event1 =
+      std::make_unique<ArcTracingEvent>(base::JSONReader::Read(kTestEvent));
+  event1->SetTimestamp(event.GetTimestamp() + 4);
+  event1->SetDuration(2);
+  EXPECT_TRUE(event.AppendChild(std::move(event1)));
+  EXPECT_EQ(1U, event.children().size());
+
+  // Impossible to append the event that is before last child.
+  std::unique_ptr<ArcTracingEvent> event2 =
+      std::make_unique<ArcTracingEvent>(base::JSONReader::Read(kTestEvent));
+  event2->SetTimestamp(event.GetTimestamp());
+  event2->SetDuration(2);
+  EXPECT_FALSE(event.AppendChild(std::move(event2)));
+  EXPECT_EQ(1U, event.children().size());
+
+  // Append child to child
+  std::unique_ptr<ArcTracingEvent> event3 =
+      std::make_unique<ArcTracingEvent>(base::JSONReader::Read(kTestEvent));
+  event3->SetTimestamp(event.GetTimestamp() + 5);
+  event3->SetDuration(1);
+  EXPECT_TRUE(event.AppendChild(std::move(event3)));
+  ASSERT_EQ(1U, event.children().size());
+  EXPECT_EQ(1U, event.children()[0].get()->children().size());
+
+  // Append next immediate child.
+  std::unique_ptr<ArcTracingEvent> event4 =
+      std::make_unique<ArcTracingEvent>(base::JSONReader::Read(kTestEvent));
+  event4->SetTimestamp(event.GetTimestamp() + 6);
+  event4->SetDuration(2);
+  EXPECT_TRUE(event.AppendChild(std::move(event4)));
+  EXPECT_EQ(2U, event.children().size());
+}
+
+TEST_F(ArcTracingModelTest, EventMatcher) {
+  const ArcTracingEvent event(base::JSONReader::Read(kTestEvent));
+  // Nothing is specified. It matches any event.
+  EXPECT_TRUE(ArcTracingEventMatcher().Match(event));
+
+  // Phase
+  EXPECT_TRUE(ArcTracingEventMatcher().SetPhase(kPhaseX).Match(event));
+  EXPECT_FALSE(ArcTracingEventMatcher().SetPhase(kPhaseP).Match(event));
+
+  // Category
+  EXPECT_TRUE(ArcTracingEventMatcher().SetCategory(kExo).Match(event));
+  EXPECT_FALSE(ArcTracingEventMatcher().SetCategory(kExoBad).Match(event));
+
+  // Name
+  EXPECT_TRUE(ArcTracingEventMatcher().SetName(kSurfaceAttach).Match(event));
+  EXPECT_FALSE(
+      ArcTracingEventMatcher().SetName(kSurfaceAttachBad).Match(event));
+
+  // Arguments
+  EXPECT_TRUE(ArcTracingEventMatcher()
+                  .AddArgument(kBufferId, kBufferIdValue)
+                  .Match(event));
+  EXPECT_TRUE(ArcTracingEventMatcher()
+                  .AddArgument(kBufferId, kBufferIdValue)
+                  .AddArgument(kAppId, kAppIdValue)
+                  .Match(event));
+  EXPECT_FALSE(ArcTracingEventMatcher()
+                   .AddArgument(kBufferIdBad, kBufferIdValue)
+                   .Match(event));
+  EXPECT_FALSE(ArcTracingEventMatcher()
+                   .AddArgument(kBufferId, kBufferIdValueBad)
+                   .Match(event));
+
+  // String query
+  EXPECT_TRUE(
+      ArcTracingEventMatcher("exo:Surface::Attach(buffer_id=0x7f9f5110690)")
+          .Match(event));
+  EXPECT_FALSE(
+      ArcTracingEventMatcher("_exo:_Surface::Attach(buffer_id=_0x7f9f5110690)")
+          .Match(event));
+}
+
+}  // namespace arc
diff --git a/chrome/test/data/arc_graphics_tracing/trace.dat.gz b/chrome/test/data/arc_graphics_tracing/trace.dat.gz
new file mode 100644
index 0000000..4c24ce7
--- /dev/null
+++ b/chrome/test/data/arc_graphics_tracing/trace.dat.gz
Binary files differ