1
2 #include "perf_data_converter.h"
3 #include "quipper/perf_parser.h"
4 #include <map>
5
6 using std::map;
7
8 namespace wireless_android_logging_awp {
9
10 typedef quipper::ParsedEvent::DSOAndOffset DSOAndOffset;
11 typedef std::vector<DSOAndOffset> callchain;
12
13 struct callchain_lt {
operator ()wireless_android_logging_awp::callchain_lt14 bool operator()(const callchain *c1, const callchain *c2) const {
15 if (c1->size() != c2->size()) {
16 return c1->size() < c2->size();
17 }
18 for (unsigned idx = 0; idx < c1->size(); ++idx) {
19 const DSOAndOffset *do1 = &(*c1)[idx];
20 const DSOAndOffset *do2 = &(*c2)[idx];
21 if (do1->offset() != do2->offset()) {
22 return do1->offset() < do2->offset();
23 }
24 int rc = do1->dso_name().compare(do2->dso_name());
25 if (rc) {
26 return rc < 0;
27 }
28 }
29 return false;
30 }
31 };
32
33 struct RangeTarget {
RangeTargetwireless_android_logging_awp::RangeTarget34 RangeTarget(uint64 start, uint64 end, uint64 to)
35 : start(start), end(end), to(to) {}
36
operator <wireless_android_logging_awp::RangeTarget37 bool operator<(const RangeTarget &r) const {
38 if (start != r.start) {
39 return start < r.start;
40 } else if (end != r.end) {
41 return end < r.end;
42 } else {
43 return to < r.to;
44 }
45 }
46 uint64 start;
47 uint64 end;
48 uint64 to;
49 };
50
51 struct BinaryProfile {
52 map<uint64, uint64> address_count_map;
53 map<RangeTarget, uint64> range_count_map;
54 map<const callchain *, uint64, callchain_lt> callchain_count_map;
55 };
56
57 wireless_android_play_playlog::AndroidPerfProfile
RawPerfDataToAndroidPerfProfile(const string & perf_file)58 RawPerfDataToAndroidPerfProfile(const string &perf_file) {
59 wireless_android_play_playlog::AndroidPerfProfile ret;
60 quipper::PerfParser parser;
61 if (!parser.ReadFile(perf_file) || !parser.ParseRawEvents()) {
62 return ret;
63 }
64
65 typedef map<string, BinaryProfile> ModuleProfileMap;
66 typedef map<string, ModuleProfileMap> ProgramProfileMap;
67
68 // Note: the callchain_count_map member in BinaryProfile contains
69 // pointers into callchains owned by "parser" above, meaning
70 // that once the parser is destroyed, callchain pointers in
71 // name_profile_map will become stale (e.g. keep these two
72 // together in the same region).
73 ProgramProfileMap name_profile_map;
74 uint64 total_samples = 0;
75 bool seen_branch_stack = false;
76 bool seen_callchain = false;
77 for (const auto &event : parser.parsed_events()) {
78 if (!event.raw_event ||
79 event.raw_event->header.type != PERF_RECORD_SAMPLE) {
80 continue;
81 }
82 string dso_name = event.dso_and_offset.dso_name();
83 string program_name = event.command();
84 const string kernel_name = "[kernel.kallsyms]";
85 if (dso_name.substr(0, kernel_name.length()) == kernel_name) {
86 dso_name = kernel_name;
87 if (program_name == "") {
88 program_name = "kernel";
89 }
90 } else if (program_name == "") {
91 program_name = "unknown_program";
92 }
93 total_samples++;
94 // We expect to see either all callchain events, all branch stack
95 // events, or all flat sample events, not a mix. For callchains,
96 // however, it can be the case that none of the IPs in a chain
97 // are mappable, in which case the parsed/mapped chain will appear
98 // empty (appearing as a flat sample).
99 if (!event.callchain.empty()) {
100 CHECK(!seen_branch_stack && "examining callchain");
101 seen_callchain = true;
102 const callchain *cc = &event.callchain;
103 name_profile_map[program_name][dso_name].callchain_count_map[cc]++;
104 } else if (!event.branch_stack.empty()) {
105 CHECK(!seen_callchain && "examining branch stack");
106 seen_branch_stack = true;
107 name_profile_map[program_name][dso_name].address_count_map[
108 event.dso_and_offset.offset()]++;
109 } else {
110 name_profile_map[program_name][dso_name].address_count_map[
111 event.dso_and_offset.offset()]++;
112 }
113 for (size_t i = 1; i < event.branch_stack.size(); i++) {
114 if (dso_name == event.branch_stack[i - 1].to.dso_name()) {
115 uint64 start = event.branch_stack[i].to.offset();
116 uint64 end = event.branch_stack[i - 1].from.offset();
117 uint64 to = event.branch_stack[i - 1].to.offset();
118 // The interval between two taken branches should not be too large.
119 if (end < start || end - start > (1 << 20)) {
120 LOG(WARNING) << "Bogus LBR data: " << start << "->" << end;
121 continue;
122 }
123 name_profile_map[program_name][dso_name].range_count_map[
124 RangeTarget(start, end, to)]++;
125 }
126 }
127 }
128
129 map<string, int> name_id_map;
130 for (const auto &program_profile : name_profile_map) {
131 for (const auto &module_profile : program_profile.second) {
132 name_id_map[module_profile.first] = 0;
133 }
134 }
135 int current_index = 0;
136 for (auto iter = name_id_map.begin(); iter != name_id_map.end(); ++iter) {
137 iter->second = current_index++;
138 }
139
140 map<string, string> name_buildid_map;
141 parser.GetFilenamesToBuildIDs(&name_buildid_map);
142 ret.set_total_samples(total_samples);
143 for (const auto &name_id : name_id_map) {
144 auto load_module = ret.add_load_modules();
145 load_module->set_name(name_id.first);
146 auto nbmi = name_buildid_map.find(name_id.first);
147 if (nbmi != name_buildid_map.end()) {
148 const std::string &build_id = nbmi->second;
149 if (build_id.size() == 40 && build_id.substr(32) == "00000000") {
150 load_module->set_build_id(build_id.substr(0, 32));
151 } else {
152 load_module->set_build_id(build_id);
153 }
154 }
155 }
156 for (const auto &program_profile : name_profile_map) {
157 auto program = ret.add_programs();
158 program->set_name(program_profile.first);
159 for (const auto &module_profile : program_profile.second) {
160 int32 module_id = name_id_map[module_profile.first];
161 auto module = program->add_modules();
162 module->set_load_module_id(module_id);
163 for (const auto &addr_count : module_profile.second.address_count_map) {
164 auto address_samples = module->add_address_samples();
165 address_samples->add_address(addr_count.first);
166 address_samples->set_count(addr_count.second);
167 }
168 for (const auto &range_count : module_profile.second.range_count_map) {
169 auto range_samples = module->add_range_samples();
170 range_samples->set_start(range_count.first.start);
171 range_samples->set_end(range_count.first.end);
172 range_samples->set_to(range_count.first.to);
173 range_samples->set_count(range_count.second);
174 }
175 for (const auto &callchain_count :
176 module_profile.second.callchain_count_map) {
177 auto address_samples = module->add_address_samples();
178 address_samples->set_count(callchain_count.second);
179 for (const auto &d_o : *callchain_count.first) {
180 int32 module_id = name_id_map[d_o.dso_name()];
181 address_samples->add_load_module_id(module_id);
182 address_samples->add_address(d_o.offset());
183 }
184 }
185 }
186 }
187 return ret;
188 }
189
190 } // namespace wireless_android_logging_awp
191