blob: 3271689a217cf8137fcf489ea9faf16992588222 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/arc/tracing/arc_system_model.h"
#include <cstdio>
#include <set>
#include "base/strings/stringprintf.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace arc {
namespace {
constexpr char kKeyCpu[] = "cpu";
constexpr char kKeyMemory[] = "memory";
constexpr char kKeyName[] = "name";
constexpr char kKeyPid[] = "pid";
constexpr char kKeyThreads[] = "threads";
bool LoadThreads(const base::Value* value,
ArcSystemModel::ThreadMap* out_threads) {
if (!value || !value->is_dict())
return false;
for (const auto it : value->DictItems()) {
int tid;
if (sscanf(it.first.c_str(), "%d", &tid) != 1)
return false;
if (!it.second.is_dict())
return false;
const std::string* name = it.second.GetDict().FindString(kKeyName);
if (!name) {
return false;
}
const absl::optional<int> pid = it.second.GetDict().FindInt(kKeyPid);
if (!pid) {
return false;
}
(*out_threads)[tid] = ArcSystemModel::ThreadInfo(pid.value(), *name);
}
return true;
}
base::Value::Dict SerializeThreads(
const ArcSystemModel::ThreadMap& threads) {
base::Value::Dict result;
for (auto& thread_info : threads) {
base::Value::Dict entry;
entry.Set(kKeyPid, base::Value(thread_info.second.pid));
entry.Set(kKeyName, base::Value(thread_info.second.name));
result.Set(base::StringPrintf("%d", thread_info.first),
std::move(entry));
}
return result;
}
template <typename T>
bool CompareByTimestampPred(const T& a, const T& b) {
return a.timestamp < b.timestamp;
}
} // namespace
ArcSystemModel::ThreadInfo::ThreadInfo() = default;
ArcSystemModel::ThreadInfo::ThreadInfo(int pid, const std::string& name)
: pid(pid), name(name) {}
bool ArcSystemModel::ThreadInfo::operator==(const ThreadInfo& other) const {
return pid == other.pid && name == other.name;
}
ArcSystemModel::ArcSystemModel() = default;
ArcSystemModel::~ArcSystemModel() = default;
void ArcSystemModel::Reset() {
thread_map_.clear();
all_cpu_events_.clear();
memory_events_.clear();
}
void ArcSystemModel::Trim(uint64_t trim_timestamp) {
const ArcCpuEvent cpu_trim_point(
trim_timestamp, ArcCpuEvent::Type::kActive /* does not matter */,
0 /* tid, does not matter */);
for (auto& cpu_events : all_cpu_events_) {
if (cpu_events.empty())
continue;
auto cpu_cut_pos =
std::lower_bound(cpu_events.begin(), cpu_events.end(), cpu_trim_point,
CompareByTimestampPred<ArcCpuEvent>);
if (cpu_cut_pos == cpu_events.begin())
continue; // Nothing to trim.
// Keep the last message for this CPU, that would be clamped to
// |trim_timestamp|.
if (cpu_cut_pos == cpu_events.end() ||
cpu_cut_pos->timestamp != trim_timestamp) {
--cpu_cut_pos;
cpu_cut_pos->timestamp = trim_timestamp;
}
cpu_events = CpuEvents(cpu_cut_pos, cpu_events.end());
}
const ArcValueEvent memory_trim_point(
trim_timestamp, ArcValueEvent::Type::kMemTotal /* does not matter */,
0 /* value, does not matter */);
auto memory_cut_pos = std::upper_bound(
memory_events_.begin(), memory_events_.end(), memory_trim_point,
CompareByTimestampPred<ArcValueEvent>);
// Keep the last message per type, that would be trimmed to |trim_timestamp|.
ValueEvents trimmed_memory_events;
std::set<ArcValueEvent::Type> trimmed_types;
auto scan_memory = memory_cut_pos;
while (scan_memory != memory_events_.begin()) {
--scan_memory;
if (!trimmed_types.count(scan_memory->type)) {
ArcValueEvent memory_event = *scan_memory;
memory_event.timestamp = trim_timestamp;
trimmed_memory_events.insert(trimmed_memory_events.begin(), memory_event);
trimmed_types.insert(memory_event.type);
}
}
// Add the rest after the trim point.
trimmed_memory_events.insert(trimmed_memory_events.end(), memory_cut_pos,
memory_events_.end());
memory_events_ = std::move(trimmed_memory_events);
}
void ArcSystemModel::CloseRangeForValueEvents(uint64_t max_timestamp) {
std::map<ArcValueEvent::Type, std::pair<uint64_t, int>> last_timestamps;
for (const auto& it : memory_events_)
last_timestamps[it.type] = {it.timestamp, it.value};
for (const auto& it : last_timestamps) {
if (it.second.first < max_timestamp)
memory_events_.emplace_back(max_timestamp, it.first, it.second.second);
}
}
void ArcSystemModel::CopyFrom(const ArcSystemModel& other) {
thread_map_ = other.thread_map_;
all_cpu_events_ = other.all_cpu_events_;
memory_events_ = other.memory_events_;
}
base::Value::Dict ArcSystemModel::Serialize() const {
base::Value::Dict result;
result.Set(kKeyThreads, SerializeThreads(thread_map_));
result.Set(kKeyCpu, SerializeAllCpuEvents(all_cpu_events_));
result.Set(kKeyMemory, SerializeValueEvents(memory_events_));
return result;
}
bool ArcSystemModel::Load(const base::Value* root) {
if (!root || !root->is_dict())
return false;
if (!LoadThreads(root->GetDict().Find(kKeyThreads), &thread_map_)) {
return false;
}
if (!LoadAllCpuEvents(root->GetDict().Find(kKeyCpu), &all_cpu_events_)) {
return false;
}
if (!LoadValueEvents(root->GetDict().Find(kKeyMemory), &memory_events_)) {
return false;
}
return true;
}
bool ArcSystemModel::operator==(const ArcSystemModel& other) const {
return thread_map_ == other.thread_map_ &&
all_cpu_events_ == other.all_cpu_events_ &&
memory_events_ == other.memory_events_;
}
} // namespace arc