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