• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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