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 sample->set_self_max_count(alloc.value.retain_max.max_count);
62 } else {
63 sample->set_self_allocated(alloc.value.totals.allocated);
64 sample->set_self_freed(alloc.value.totals.freed);
65
66 sample->set_alloc_count(alloc.value.totals.allocation_count);
67 sample->set_free_count(alloc.value.totals.free_count);
68 }
69 }
70
DumpCallstacks(GlobalCallstackTrie * callsites)71 void DumpState::DumpCallstacks(GlobalCallstackTrie* callsites) {
72 // We need a way to signal to consumers when they have fully consumed the
73 // InternedData they need to understand the sequence of continued
74 // ProfilePackets. The way we do that is to mark the last ProfilePacket as
75 // continued, then emit the InternedData, and then an empty ProfilePacket
76 // to terminate the sequence.
77 //
78 // This is why we set_continued at the beginning of this function, and
79 // MakeProfilePacket at the end.
80 if (current_trace_packet_)
81 current_profile_packet_->set_continued(true);
82 for (GlobalCallstackTrie::Node* node : callstacks_to_dump_) {
83 intern_state_->WriteCallstack(node, callsites, GetCurrentInternedData());
84 }
85 MakeProfilePacket();
86 }
87
GetCurrentProcessHeapSamples()88 ProfilePacket::ProcessHeapSamples* DumpState::GetCurrentProcessHeapSamples() {
89 if (currently_written() > kPacketSizeThreshold) {
90 if (current_profile_packet_)
91 current_profile_packet_->set_continued(true);
92 MakeProfilePacket();
93 }
94
95 if (current_process_heap_samples_ == nullptr) {
96 current_process_heap_samples_ =
97 current_profile_packet_->add_process_dumps();
98 current_process_fill_header_(current_process_heap_samples_);
99 }
100
101 return current_process_heap_samples_;
102 }
103
GetCurrentInternedData()104 protos::pbzero::InternedData* DumpState::GetCurrentInternedData() {
105 if (currently_written() > kPacketSizeThreshold)
106 MakeTracePacket();
107
108 if (current_interned_data_ == nullptr)
109 current_interned_data_ = current_trace_packet_->set_interned_data();
110
111 return current_interned_data_;
112 }
113
114 } // namespace profiling
115 } // namespace perfetto
116