5 // Created by James McIlree on 4/23/13.
6 // Copyright (c) 2014 Apple. All rights reserved.
9 #ifndef kdprof_ProcessSummary_hpp
10 #define kdprof_ProcessSummary_hpp
12 template <typename SIZE>
15 template <typename SIZE>
18 template <typename SIZE>
21 template <typename SIZE>
22 class ProcessSummary {
24 typedef std::unordered_set<ThreadSummary<SIZE>, ThreadSummaryHash<SIZE>, ThreadSummaryEqualTo<SIZE>> ThreadSummarySet;
27 const MachineProcess<SIZE>* _process;
29 AbsTime _total_run_time;
30 AbsTime _total_idle_time;
31 AbsTime _total_intr_time;
32 AbsTime _total_future_run_time;
33 AbsTime _total_wallclock_run_time;
34 AbsTime _total_vm_fault_time;
35 AbsTime _total_io_time;
36 AbsTime _total_jetsam_time;
38 uint32_t _context_switch_count;
39 uint32_t _count_idle_events;
40 uint32_t _count_intr_events;
41 uint32_t _count_vm_fault_events;
42 uint32_t _count_io_events;
43 bool _is_jetsam_killed;
45 uint64_t _io_bytes_completed;
47 ThreadSummarySet _thread_summaries;
49 std::vector<AbsInterval> _wallclock_run_intervals; // This is the actual wallclock run interval data.
50 std::vector<AbsInterval> _per_cpu_wallclock_run_intervals; // We need to accumulate intervals during summary generation, this is a temp buffer.
52 friend class Machine<SIZE>;
53 friend class CPUSummary<SIZE>;
55 void add_run_time(AbsTime time) { _total_run_time += time; }
56 void add_idle_time(AbsTime time) { _total_idle_time += time; _count_idle_events++; }
57 void add_intr_time(AbsTime time) { _total_intr_time += time; _count_intr_events++; }
58 void add_future_run_time(AbsTime time) { _total_future_run_time += time; }
59 void add_vm_fault_time(AbsTime time) { _total_vm_fault_time += time; _count_vm_fault_events++; }
60 void add_io_time(AbsTime time) { _total_io_time += time; _count_io_events++; }
61 void add_jetsam_time(AbsTime time) { _total_jetsam_time += time; }
63 void add_io_bytes_completed(typename SIZE::ptr_t bytes) { _io_bytes_completed += bytes; }
66 // Wallclock run intervals are added as each cpu timeline is walked.
67 // Between cpu(s), the results are accumulated to a single buffer
68 // After all cpus have been processed, the single buffer is summarized
70 void add_wallclock_run_interval(AbsInterval interval);
71 void accumulate_wallclock_run_intervals();
72 void summarize_wallclock_run_intervals();
74 void incr_context_switches() { _context_switch_count++; }
76 void set_jetsam_killed() { ASSERT(!_is_jetsam_killed, "Attempt to jetsam process twice"); _is_jetsam_killed = true; }
78 ThreadSummary<SIZE>* mutable_thread_summary(const MachineThread<SIZE>* thread) {
79 auto it = _thread_summaries.find(thread);
80 if (it == _thread_summaries.end()) {
81 // We create any thread summary that is missing.
82 auto insert_result = _thread_summaries.emplace(thread);
83 ASSERT(insert_result.second, "Sanity");
84 it = insert_result.first;
87 // NOTE! Because we are using a Set instead of a Map, STL wants
88 // the objects to be immutable. "it" refers to a const Record, to
89 // prevent us from changing the hash or equality of the Set. We
90 // know that the allowed set of mutations will not change these,
91 // and so we evil hack(tm) and cast away the const'ness.
92 return const_cast<ThreadSummary<SIZE>*>(&*it);
95 ThreadSummarySet& mutable_thread_summaries() { return _thread_summaries; }
98 ProcessSummary(const MachineProcess<SIZE>* process) :
100 _context_switch_count(0),
101 _count_idle_events(0),
102 _count_intr_events(0),
103 _count_vm_fault_events(0),
105 _is_jetsam_killed(false),
106 _io_bytes_completed(0)
110 const MachineProcess<SIZE>* process() const { return _process; }
112 AbsTime total_time() const { return _total_run_time + _total_idle_time + _total_intr_time; }
113 AbsTime total_run_time() const { return _total_run_time; }
114 AbsTime total_idle_time() const { return _total_idle_time; }
115 AbsTime total_intr_time() const { return _total_intr_time; }
116 AbsTime total_future_run_time() const { return _total_future_run_time; }
117 AbsTime total_wallclock_run_time() const { return _total_wallclock_run_time; }
118 AbsTime total_vm_fault_time() const { return _total_vm_fault_time; }
119 AbsTime total_io_time() const { return _total_io_time; }
120 AbsTime total_jetsam_time() const { return _total_jetsam_time; }
122 AbsTime avg_on_cpu_time() const { return _total_run_time / _context_switch_count; }
124 uint32_t context_switches() const { return _context_switch_count; }
125 uint32_t num_idle_events() const { return _count_idle_events; }
126 uint32_t num_intr_events() const { return _count_intr_events; }
127 uint32_t num_vm_fault_events() const { return _count_vm_fault_events; }
128 uint32_t num_io_events() const { return _count_io_events; }
129 uint32_t num_processes_jetsammed() const { return _is_jetsam_killed ? 1 : 0; }
131 uint64_t io_bytes_completed() const { return _io_bytes_completed; }
133 const ThreadSummarySet& thread_summaries() const { return _thread_summaries; }
135 const ThreadSummary<SIZE>* thread_summary(const MachineThread<SIZE>* thread) const {
136 auto it = _thread_summaries.find(thread);
137 return (it == _thread_summaries.end()) ? NULL : &*it;
140 DEBUG_ONLY(void validate() const;)
143 template <typename SIZE>
144 void ProcessSummary<SIZE>::add_wallclock_run_interval(AbsInterval interval) {
145 ASSERT(_per_cpu_wallclock_run_intervals.empty() || (_per_cpu_wallclock_run_intervals.back() < interval && !interval.intersects(_per_cpu_wallclock_run_intervals.back())), "Invariant violated");
146 _per_cpu_wallclock_run_intervals.emplace_back(interval);
149 template <typename SIZE>
150 void ProcessSummary<SIZE>::accumulate_wallclock_run_intervals() {
151 _wallclock_run_intervals = trange_vector_union(_wallclock_run_intervals, _per_cpu_wallclock_run_intervals);
152 _per_cpu_wallclock_run_intervals.clear();
153 // We don't shrink_to_fit here as its expected another CPU's run intervals will be processed next.
156 template <typename SIZE>
157 void ProcessSummary<SIZE>::summarize_wallclock_run_intervals() {
158 ASSERT(_per_cpu_wallclock_run_intervals.empty(), "Sanity");
159 _per_cpu_wallclock_run_intervals.shrink_to_fit();
161 ASSERT(_total_wallclock_run_time == 0, "Called more than once");
163 ASSERT(is_trange_vector_sorted_and_non_overlapping(_wallclock_run_intervals), "Sanity");
165 for (auto& interval : _wallclock_run_intervals) {
166 _total_wallclock_run_time += interval.length();
169 _wallclock_run_intervals.clear();
170 _wallclock_run_intervals.shrink_to_fit();
173 #if !defined(NDEBUG) && !defined(NS_BLOCK_ASSERTIONS)
174 template <typename SIZE>
175 void ProcessSummary<SIZE>::validate() const {
176 ASSERT(_total_wallclock_run_time <= _total_run_time, "Sanity");
178 for (const auto& thread_summary : _thread_summaries) {
179 thread_summary.validate();
184 template <typename SIZE>
185 struct ProcessSummaryHash {
186 size_t operator()(const ProcessSummary<SIZE>& summary) const {
187 return std::hash<const MachineProcess<SIZE>*>()(summary.process());
191 template <typename SIZE>
192 struct ProcessSummaryEqualTo {
193 bool operator()(const ProcessSummary<SIZE>& s1, const ProcessSummary<SIZE>& s2) const {
194 return s1.process() == s2.process();