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