1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/profiling/memory/bookkeeping_dump.h"
18
19 namespace perfetto {
20 namespace profiling {
21 namespace {
22 using ::perfetto::protos::pbzero::ProfilePacket;
23 // This needs to be lower than the maximum acceptable chunk size, because this
24 // is checked *before* writing another submessage. We conservatively assume
25 // submessages can be up to 100k here for a 500k chunk size.
26 // DropBox has a 500k chunk limit, and each chunk needs to parse as a proto.
27 uint32_t kPacketSizeThreshold = 400000;
28 } // namespace
29
WriteMap(const Interned<Mapping> map)30 void DumpState::WriteMap(const Interned<Mapping> map) {
31 intern_state_->WriteMap(map, GetCurrentInternedData());
32 }
33
WriteFrame(Interned<Frame> frame)34 void DumpState::WriteFrame(Interned<Frame> frame) {
35 intern_state_->WriteFrame(frame, GetCurrentInternedData());
36 }
37
WriteBuildIDString(const Interned<std::string> & str)38 void DumpState::WriteBuildIDString(const Interned<std::string>& str) {
39 intern_state_->WriteBuildIDString(str, GetCurrentInternedData());
40 }
41
WriteMappingPathString(const Interned<std::string> & str)42 void DumpState::WriteMappingPathString(const Interned<std::string>& str) {
43 intern_state_->WriteMappingPathString(str, GetCurrentInternedData());
44 }
45
WriteFunctionNameString(const Interned<std::string> & str)46 void DumpState::WriteFunctionNameString(const Interned<std::string>& str) {
47 intern_state_->WriteFunctionNameString(str, GetCurrentInternedData());
48 }
49
WriteAllocation(const HeapTracker::CallstackAllocations & alloc,bool dump_at_max_mode)50 void DumpState::WriteAllocation(const HeapTracker::CallstackAllocations& alloc,
51 bool dump_at_max_mode) {
52 if (intern_state_->IsCallstackNew(alloc.node->id())) {
53 callstacks_to_dump_.emplace(alloc.node);
54 }
55
56 auto* heap_samples = GetCurrentProcessHeapSamples();
57 ProfilePacket::HeapSample* sample = heap_samples->add_samples();
58 sample->set_callstack_id(alloc.node->id());
59 if (dump_at_max_mode) {
60 sample->set_self_max(alloc.value.retain_max.max);
61 } else {
62 sample->set_self_allocated(alloc.value.totals.allocated);
63 sample->set_self_freed(alloc.value.totals.freed);
64 }
65 sample->set_alloc_count(alloc.allocation_count);
66 sample->set_free_count(alloc.free_count);
67
68 auto it = current_process_idle_allocs_.find(alloc.node->id());
69 if (it != current_process_idle_allocs_.end())
70 sample->set_self_idle(it->second);
71 }
72
DumpCallstacks(GlobalCallstackTrie * callsites)73 void DumpState::DumpCallstacks(GlobalCallstackTrie* callsites) {
74 // We need a way to signal to consumers when they have fully consumed the
75 // InternedData they need to understand the sequence of continued
76 // ProfilePackets. The way we do that is to mark the last ProfilePacket as
77 // continued, then emit the InternedData, and then an empty ProfilePacket
78 // to terminate the sequence.
79 //
80 // This is why we set_continued at the beginning of this function, and
81 // MakeProfilePacket at the end.
82 if (current_trace_packet_)
83 current_profile_packet_->set_continued(true);
84 for (GlobalCallstackTrie::Node* node : callstacks_to_dump_) {
85 intern_state_->WriteCallstack(node, callsites, GetCurrentInternedData());
86 }
87 MakeProfilePacket();
88 }
89
AddIdleBytes(uint64_t callstack_id,uint64_t bytes)90 void DumpState::AddIdleBytes(uint64_t callstack_id, uint64_t bytes) {
91 current_process_idle_allocs_[callstack_id] += bytes;
92 }
93
GetCurrentProcessHeapSamples()94 ProfilePacket::ProcessHeapSamples* DumpState::GetCurrentProcessHeapSamples() {
95 if (currently_written() > kPacketSizeThreshold) {
96 if (current_profile_packet_)
97 current_profile_packet_->set_continued(true);
98 MakeProfilePacket();
99 }
100
101 if (current_process_heap_samples_ == nullptr) {
102 current_process_heap_samples_ =
103 current_profile_packet_->add_process_dumps();
104 current_process_fill_header_(current_process_heap_samples_);
105 }
106
107 return current_process_heap_samples_;
108 }
109
GetCurrentInternedData()110 protos::pbzero::InternedData* DumpState::GetCurrentInternedData() {
111 if (currently_written() > kPacketSizeThreshold)
112 MakeTracePacket();
113
114 if (current_interned_data_ == nullptr)
115 current_interned_data_ = current_trace_packet_->set_interned_data();
116
117 return current_interned_data_;
118 }
119
120 } // namespace profiling
121 } // namespace perfetto
122