• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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/common/interning_output.h"
18 
19 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
20 #include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
21 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
22 #include "protos/perfetto/trace/trace_packet.pbzero.h"
23 
24 namespace {
25 // Flags used to distinguish distinct types of interned strings.
26 constexpr int kDumpedBuildID = 1 << 0;
27 constexpr int kDumpedMappingPath = 1 << 1;
28 constexpr int kDumpedFunctionName = 1 << 2;
29 }  // namespace
30 
31 namespace perfetto {
32 namespace profiling {
33 
34 // static
WriteFixedInterningsPacket(TraceWriter * trace_writer,uint32_t sequence_flags)35 void InterningOutputTracker::WriteFixedInterningsPacket(
36     TraceWriter* trace_writer,
37     uint32_t sequence_flags) {
38   constexpr const uint8_t kEmptyString[] = "";
39   // Explicitly reserve intern ID 0 for the empty string, so unset string
40   // fields get mapped to this.
41   auto packet = trace_writer->NewTracePacket();
42   auto* interned_data = packet->set_interned_data();
43   auto* interned_string = interned_data->add_build_ids();
44   interned_string->set_iid(0);
45   interned_string->set_str(kEmptyString, 0);
46 
47   interned_string = interned_data->add_mapping_paths();
48   interned_string->set_iid(0);
49   interned_string->set_str(kEmptyString, 0);
50 
51   interned_string = interned_data->add_function_names();
52   interned_string->set_iid(0);
53   interned_string->set_str(kEmptyString, 0);
54 
55   if (sequence_flags) {
56     packet->set_sequence_flags(sequence_flags);
57   }
58 }
59 
WriteMap(const Interned<Mapping> map,protos::pbzero::InternedData * out)60 void InterningOutputTracker::WriteMap(const Interned<Mapping> map,
61                                       protos::pbzero::InternedData* out) {
62   auto map_it_and_inserted = dumped_mappings_.emplace(map.id());
63   if (map_it_and_inserted.second) {
64     for (const Interned<std::string>& str : map->path_components)
65       WriteMappingPathString(str, out);
66 
67     WriteBuildIDString(map->build_id, out);
68 
69     protos::pbzero::Mapping* mapping = out->add_mappings();
70     mapping->set_iid(map.id());
71     mapping->set_exact_offset(map->exact_offset);
72     mapping->set_start_offset(map->start_offset);
73     mapping->set_start(map->start);
74     mapping->set_end(map->end);
75     mapping->set_load_bias(map->load_bias);
76     mapping->set_build_id(map->build_id.id());
77     for (const Interned<std::string>& str : map->path_components)
78       mapping->add_path_string_ids(str.id());
79   }
80 }
81 
WriteFrame(Interned<Frame> frame,protos::pbzero::InternedData * out)82 void InterningOutputTracker::WriteFrame(Interned<Frame> frame,
83                                         protos::pbzero::InternedData* out) {
84   // Trace processor depends on the map being written before the
85   // frame. See StackProfileTracker::AddFrame.
86   WriteMap(frame->mapping, out);
87   WriteFunctionNameString(frame->function_name, out);
88   bool inserted;
89   std::tie(std::ignore, inserted) = dumped_frames_.emplace(frame.id());
90   if (inserted) {
91     protos::pbzero::Frame* frame_proto = out->add_frames();
92     frame_proto->set_iid(frame.id());
93     frame_proto->set_function_name_id(frame->function_name.id());
94     frame_proto->set_mapping_id(frame->mapping.id());
95     frame_proto->set_rel_pc(frame->rel_pc);
96   }
97 }
98 
WriteBuildIDString(const Interned<std::string> & str,protos::pbzero::InternedData * out)99 void InterningOutputTracker::WriteBuildIDString(
100     const Interned<std::string>& str,
101     protos::pbzero::InternedData* out) {
102   auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
103   auto it = it_and_inserted.first;
104   // This is for the rare case that the same string is used as two different
105   // types (e.g. a function name that matches a path segment). In that case
106   // we need to emit the string as all of its types.
107   if ((it->second & kDumpedBuildID) == 0) {
108     protos::pbzero::InternedString* interned_string = out->add_build_ids();
109     interned_string->set_iid(str.id());
110     interned_string->set_str(str.data());
111     it->second |= kDumpedBuildID;
112   }
113 }
114 
WriteMappingPathString(const Interned<std::string> & str,protos::pbzero::InternedData * out)115 void InterningOutputTracker::WriteMappingPathString(
116     const Interned<std::string>& str,
117     protos::pbzero::InternedData* out) {
118   auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
119   auto it = it_and_inserted.first;
120   // This is for the rare case that the same string is used as two different
121   // types (e.g. a function name that matches a path segment). In that case
122   // we need to emit the string as all of its types.
123   if ((it->second & kDumpedMappingPath) == 0) {
124     protos::pbzero::InternedString* interned_string = out->add_mapping_paths();
125     interned_string->set_iid(str.id());
126     interned_string->set_str(str.data());
127     it->second |= kDumpedMappingPath;
128   }
129 }
130 
WriteFunctionNameString(const Interned<std::string> & str,protos::pbzero::InternedData * out)131 void InterningOutputTracker::WriteFunctionNameString(
132     const Interned<std::string>& str,
133     protos::pbzero::InternedData* out) {
134   auto it_and_inserted = dumped_strings_.emplace(str.id(), 0);
135   auto it = it_and_inserted.first;
136   // This is for the rare case that the same string is used as two different
137   // types (e.g. a function name that matches a path segment). In that case
138   // we need to emit the string as all of its types.
139   if ((it->second & kDumpedFunctionName) == 0) {
140     protos::pbzero::InternedString* interned_string = out->add_function_names();
141     interned_string->set_iid(str.id());
142     interned_string->set_str(str.data());
143     it->second |= kDumpedFunctionName;
144   }
145 }
146 
WriteCallstack(GlobalCallstackTrie::Node * node,GlobalCallstackTrie * trie,protos::pbzero::InternedData * out)147 void InterningOutputTracker::WriteCallstack(GlobalCallstackTrie::Node* node,
148                                             GlobalCallstackTrie* trie,
149                                             protos::pbzero::InternedData* out) {
150   bool inserted;
151   std::tie(std::ignore, inserted) = dumped_callstacks_.emplace(node->id());
152   if (inserted) {
153     // There need to be two separate loops over built_callstack because
154     // protozero cannot interleave different messages.
155     auto built_callstack = trie->BuildInverseCallstack(node);
156     for (const Interned<Frame>& frame : built_callstack)
157       WriteFrame(frame, out);
158 
159     protos::pbzero::Callstack* callstack = out->add_callstacks();
160     callstack->set_iid(node->id());
161     for (auto frame_it = built_callstack.crbegin();
162          frame_it != built_callstack.crend(); ++frame_it) {
163       const Interned<Frame>& frame = *frame_it;
164       callstack->add_frame_ids(frame.id());
165     }
166   }
167 }
168 
ClearHistory()169 void InterningOutputTracker::ClearHistory() {
170   dumped_strings_.clear();
171   dumped_frames_.clear();
172   dumped_mappings_.clear();
173   dumped_callstacks_.clear();
174 }
175 
176 }  // namespace profiling
177 }  // namespace perfetto
178