| // 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 |