1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "partition_alloc/starscan/stats_collector.h"
6 
7 #include "partition_alloc/internal_allocator.h"
8 #include "partition_alloc/partition_alloc_base/time/time.h"
9 #include "partition_alloc/starscan/logging.h"
10 #include "partition_alloc/starscan/stats_reporter.h"
11 
12 namespace partition_alloc::internal {
13 
StatsCollector(const char * process_name,size_t quarantine_last_size)14 StatsCollector::StatsCollector(const char* process_name,
15                                size_t quarantine_last_size)
16     : process_name_(process_name),
17       quarantine_last_size_(quarantine_last_size) {}
18 
19 StatsCollector::~StatsCollector() = default;
20 
GetOverallTime() const21 base::TimeDelta StatsCollector::GetOverallTime() const {
22   return GetTimeImpl<Context::kMutator>(mutator_trace_events_,
23                                         MutatorId::kOverall) +
24          GetTimeImpl<Context::kScanner>(scanner_trace_events_,
25                                         ScannerId::kOverall);
26 }
27 
ReportTracesAndHists(partition_alloc::StatsReporter & reporter) const28 void StatsCollector::ReportTracesAndHists(
29     partition_alloc::StatsReporter& reporter) const {
30   ReportTracesAndHistsImpl<Context::kMutator>(reporter, mutator_trace_events_);
31   ReportTracesAndHistsImpl<Context::kScanner>(reporter, scanner_trace_events_);
32   ReportSurvivalRate(reporter);
33 }
34 
35 template <Context context>
GetTimeImpl(const DeferredTraceEventMap<context> & event_map,IdType<context> id) const36 base::TimeDelta StatsCollector::GetTimeImpl(
37     const DeferredTraceEventMap<context>& event_map,
38     IdType<context> id) const {
39   base::TimeDelta overall;
40   for (const auto& tid_and_events : event_map.get_underlying_map_unsafe()) {
41     const auto& events = tid_and_events.second;
42     const auto& event = events[static_cast<size_t>(id)];
43     overall += (event.end_time - event.start_time);
44   }
45   return overall;
46 }
47 
48 template <Context context>
ReportTracesAndHistsImpl(partition_alloc::StatsReporter & reporter,const DeferredTraceEventMap<context> & event_map) const49 void StatsCollector::ReportTracesAndHistsImpl(
50     partition_alloc::StatsReporter& reporter,
51     const DeferredTraceEventMap<context>& event_map) const {
52   std::array<base::TimeDelta, static_cast<size_t>(IdType<context>::kNumIds)>
53       accumulated_events{};
54   // First, report traces and accumulate each trace scope to report UMA hists.
55   for (const auto& tid_and_events : event_map.get_underlying_map_unsafe()) {
56     const internal::base::PlatformThreadId tid = tid_and_events.first;
57     const auto& events = tid_and_events.second;
58     PA_DCHECK(accumulated_events.size() == events.size());
59     for (size_t id = 0; id < events.size(); ++id) {
60       const auto& event = events[id];
61       if (event.start_time.is_null()) {
62         // If start_time is null, the event was never triggered, e.g. safepoint
63         // bailed out if started at the end of scanning.
64         PA_DCHECK(event.end_time.is_null());
65         continue;
66       }
67       reporter.ReportTraceEvent(static_cast<IdType<context>>(id), tid,
68                                 event.start_time.ToInternalValue(),
69                                 event.end_time.ToInternalValue());
70       accumulated_events[id] += (event.end_time - event.start_time);
71     }
72   }
73   // Report UMA if process_name is set.
74   if (!process_name_) {
75     return;
76   }
77   for (size_t id = 0; id < accumulated_events.size(); ++id) {
78     if (accumulated_events[id].is_zero()) {
79       continue;
80     }
81     reporter.ReportStats(ToUMAString(static_cast<IdType<context>>(id)).c_str(),
82                          accumulated_events[id].InMicroseconds());
83   }
84 }
85 
ReportSurvivalRate(partition_alloc::StatsReporter & reporter) const86 void StatsCollector::ReportSurvivalRate(
87     partition_alloc::StatsReporter& reporter) const {
88   const double survived_rate =
89       static_cast<double>(survived_quarantine_size()) / quarantine_last_size_;
90   reporter.ReportSurvivedQuarantineSize(survived_quarantine_size());
91   reporter.ReportSurvivedQuarantinePercent(survived_rate);
92   PA_PCSCAN_VLOG(2) << "quarantine size: " << quarantine_last_size_ << " -> "
93                     << survived_quarantine_size()
94                     << ", swept bytes: " << swept_size()
95                     << ", survival rate: " << survived_rate;
96   if (discarded_quarantine_size_) {
97     PA_PCSCAN_VLOG(2) << "discarded quarantine size: "
98                       << discarded_quarantine_size_;
99   }
100 }
101 
102 template base::TimeDelta StatsCollector::GetTimeImpl(
103     const DeferredTraceEventMap<Context::kMutator>&,
104     IdType<Context::kMutator>) const;
105 template base::TimeDelta StatsCollector::GetTimeImpl(
106     const DeferredTraceEventMap<Context::kScanner>&,
107     IdType<Context::kScanner>) const;
108 
109 template void StatsCollector::ReportTracesAndHistsImpl(
110     partition_alloc::StatsReporter& reporter,
111     const DeferredTraceEventMap<Context::kMutator>&) const;
112 template void StatsCollector::ReportTracesAndHistsImpl(
113     partition_alloc::StatsReporter& reporter,
114     const DeferredTraceEventMap<Context::kScanner>&) const;
115 
116 }  // namespace partition_alloc::internal
117