1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
#include "perf_data_converter.h"
#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <unordered_map>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
#include <perf_data_utils.h>
#include <perf_parser.h>
#include <perf_protobuf_io.h>
#include "perfprofd_record.pb.h"
#include "perf_data.pb.h"
#include "map_utils.h"
#include "quipper_helper.h"
#include "symbolizer.h"
using std::map;
namespace android {
namespace perfprofd {
namespace {
void AddSymbolInfo(PerfprofdRecord* record,
::quipper::PerfParser& perf_parser,
::perfprofd::Symbolizer* symbolizer,
bool symbolize_everything) {
std::unordered_set<std::string> filenames_w_build_id;
if (!symbolize_everything) {
for (auto& perf_build_id : record->build_ids()) {
filenames_w_build_id.insert(perf_build_id.filename());
}
}
std::unordered_set<std::string> files_wo_build_id;
{
quipper::MmapEventIterator it(*record);
for (; it != it.end(); ++it) {
const ::quipper::PerfDataProto_MMapEvent* mmap_event = &it->mmap_event();
if (!mmap_event->has_filename() || !mmap_event->has_start() || !mmap_event->has_len()) {
// Don't care.
continue;
}
if (filenames_w_build_id.count(mmap_event->filename()) == 0) {
files_wo_build_id.insert(mmap_event->filename());
}
}
}
if (files_wo_build_id.empty()) {
return;
}
struct Dso {
uint64_t min_vaddr;
RangeMap<std::string, uint64_t> symbols;
explicit Dso(uint64_t min_vaddr_in) : min_vaddr(min_vaddr_in) {
}
};
std::unordered_map<std::string, Dso> files;
auto it = record->events().begin();
auto end = record->events().end();
auto parsed_it = perf_parser.parsed_events().begin();
auto parsed_end = perf_parser.parsed_events().end();
for (; it != end; ++it, ++parsed_it) {
CHECK(parsed_it != parsed_end);
if (!it->has_sample_event()) {
continue;
}
const ::quipper::PerfDataProto_SampleEvent& sample_event = it->sample_event();
if (android::base::kEnableDChecks) {
// Check that the parsed_event and sample_event are consistent.
CHECK_EQ(parsed_it->callchain.size(), sample_event.callchain_size());
}
auto check_address = [&](const std::string& dso_name, uint64_t offset) {
if (files_wo_build_id.count(dso_name) == 0) {
return;
}
// OK, that's a hit in the mmap segment (w/o build id).
Dso* dso_data;
{
auto dso_it = files.find(dso_name);
constexpr uint64_t kNoMinAddr = std::numeric_limits<uint64_t>::max();
if (dso_it == files.end()) {
uint64_t min_vaddr;
bool has_min_vaddr = symbolizer->GetMinExecutableVAddr(dso_name, &min_vaddr);
if (!has_min_vaddr) {
min_vaddr = kNoMinAddr;
}
auto it = files.emplace(dso_name, Dso(min_vaddr));
dso_data = &it.first->second;
} else {
dso_data = &dso_it->second;
}
if (dso_data->min_vaddr == kNoMinAddr) {
return;
}
}
// TODO: Is min_vaddr necessary here?
const uint64_t file_addr = offset;
std::string symbol = symbolizer->Decode(dso_name, file_addr);
if (symbol.empty()) {
return;
}
dso_data->symbols.Insert(symbol, file_addr);
};
if (sample_event.has_ip() && parsed_it->dso_and_offset.dso_info_ != nullptr) {
check_address(parsed_it->dso_and_offset.dso_info_->name, parsed_it->dso_and_offset.offset_);
}
if (sample_event.callchain_size() > 0) {
for (auto& callchain_data: parsed_it->callchain) {
if (callchain_data.dso_info_ == nullptr) {
continue;
}
check_address(callchain_data.dso_info_->name, callchain_data.offset_);
}
}
}
if (!files.empty()) {
// We have extra symbol info, create proto messages now.
size_t symbol_info_index = 0;
for (auto& file_data : files) {
const std::string& filename = file_data.first;
const Dso& dso = file_data.second;
if (dso.symbols.empty()) {
continue;
}
auto* symbol_info = record->AddExtension(::quipper::symbol_info);
symbol_info->set_filename(filename);
symbol_info->set_filename_md5_prefix(::quipper::Md5Prefix(filename));
symbol_info->set_min_vaddr(dso.min_vaddr);
for (auto& aggr_sym : dso.symbols) {
auto* symbol = symbol_info->add_symbols();
symbol->set_addr(*aggr_sym.second.offsets.begin());
symbol->set_size(*aggr_sym.second.offsets.rbegin() - *aggr_sym.second.offsets.begin() + 1);
symbol->set_name(aggr_sym.second.symbol);
symbol->set_name_md5_prefix(::quipper::Md5Prefix(aggr_sym.second.symbol));
}
++symbol_info_index;
}
}
}
} // namespace
PerfprofdRecord*
RawPerfDataToAndroidPerfProfile(const string &perf_file,
::perfprofd::Symbolizer* symbolizer,
bool symbolize_everything) {
std::unique_ptr<PerfprofdRecord> ret(new PerfprofdRecord());
ret->SetExtension(::quipper::id, 0); // TODO.
::quipper::PerfParserOptions options = {};
options.do_remap = true;
options.discard_unused_events = true;
options.read_missing_buildids = true;
::quipper::PerfReader reader;
if (!reader.ReadFile(perf_file)) return nullptr;
::quipper::PerfParser parser(&reader, options);
if (!parser.ParseRawEvents()) return nullptr;
if (!reader.Serialize(ret.get())) return nullptr;
// Append parser stats to protobuf.
::quipper::PerfSerializer::SerializeParserStats(parser.stats(), ret.get());
// TODO: Symbolization.
if (symbolizer != nullptr) {
AddSymbolInfo(ret.get(), parser, symbolizer, symbolize_everything);
}
return ret.release();
}
} // namespace perfprofd
} // namespace android
|