* Copyright (c) 2021-2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define HILOG_TAG "Protobuf"
#include "report_protobuf_file.h"
#include "utilities.h"
using namespace Proto;
namespace OHOS {
namespace Developtools {
namespace HiPerf {
ReportProtobufFileWriter::~ReportProtobufFileWriter()
{
Close();
}
void ReportProtobufFileWriter::BeforeClose()
{
HiperfRecord record;
SampleStatistic *message = record.mutable_statistic();
message->set_count(recordCount_);
message->set_lost(recordLost_);
protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
}
void ReportProtobufFileWriter::Close()
{
if (protobufFileStream_ == nullptr) {
HLOGE("protobufFileStream_ is nullptr");
return;
}
if (protobufFileStream_->is_open()) {
BeforeClose();
protpbufCodedOutputStream_->WriteLittleEndian32(0);
protpbufCodedOutputStream_.reset(nullptr);
protpbufOutputStream_.reset(nullptr);
protobufFileStream_->close();
}
}
bool ReportProtobufFileWriter::Write(const void *buffer, const int size)
{
if (protobufFileStream_ == nullptr) {
HLOGE("protobufFileStream_ is nullptr");
return false;
}
if (protobufFileStream_->is_open()) {
try {
protobufFileStream_->write(static_cast<const char *>(buffer), size);
HLOGM("writed %d bytes", size);
return true;
} catch (std::ofstream::failure &writeErr) {
HLOGE("write file failed %s", fileName_.c_str());
}
} else {
printf("no file open for write (request %d bytes).\n", size);
}
return false;
}
bool ReportProtobufFileWriter::Create(const std::string fileName)
{
fileName_ = fileName;
try {
if (protobufFileStream_ == nullptr) {
HLOGE("protobufFileStream_ is nullptr");
return false;
}
protobufFileStream_->exceptions(std::ofstream::failbit | std::ofstream::badbit |
std::ofstream::eofbit);
std::string resolvedPath = CanonicalizeSpecPath(fileName_.c_str());
protobufFileStream_->open(resolvedPath.c_str(),
std::fstream::out | std::fstream::trunc | std::fstream::binary);
protpbufOutputStream_ =
std::make_unique<google::protobuf::io::CopyingOutputStreamAdaptor>(this);
protpbufCodedOutputStream_ =
std::make_unique<google::protobuf::io::CodedOutputStream>(protpbufOutputStream_.get());
printf("open proto buf file succeed.\n");
Write(FILE_MAGIC, sizeof(FILE_MAGIC) - 1);
Write(&FILE_VERSION, sizeof(FILE_VERSION));
printf("create proto buf file succeed.\n");
return true;
} catch (const std::fstream::failure &e) {
printf("open proto buf file faild. %s\n", e.what());
}
return false;
}
bool ReportProtobufFileWriter::IsOpen()
{
return protobufFileStream_->is_open();
}
bool ReportProtobufFileWriter::ProcessRecord(const PerfEventRecord &record)
{
HLOGM("ProcessRecord %d", record.GetType());
if (record.GetType() == PERF_RECORD_SAMPLE) {
HLOGF("record.GetType() == PERF_RECORD_SAMPLE");
} else if (record.GetType() == PERF_RECORD_LOST_SAMPLES) {
ProcessRecord(*static_cast<const PerfRecordLost *>(&record));
} else if (record.GetType() == PERF_RECORD_COMM) {
ProcessRecord(*static_cast<const PerfRecordComm *>(&record));
} else {
HLOGM("skip record type %d", record.GetType());
return false;
}
return true;
}
bool ReportProtobufFileWriter::ProcessSampleRecord(
const PerfRecordSample &recordSample, uint32_t configIndex,
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
{
HiperfRecord record;
CallStackSample *sample = record.mutable_sample();
sample->set_time(recordSample.data_.time);
sample->set_tid(recordSample.data_.tid);
for (const DfxFrame &frame : recordSample.callFrames_) {
auto callframe = sample->add_callstackframe();
callframe->set_symbols_vaddr(frame.funcOffset);
callframe->set_loaded_vaddr(frame.pc - frame.mapOffset);
if (frame.symbolFileIndex >= 0) {
callframe->set_symbols_file_id(frame.symbolFileIndex);
callframe->set_function_name_id(frame.index);
}
}
sample->set_event_count(recordSample.data_.period);
sample->set_config_name_id(configIndex);
protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
recordCount_++;
return true;
}
bool ReportProtobufFileWriter::ProcessReportInfo(const std::vector<std::string> &configNames,
const std::string &workloadCmd)
{
HiperfRecord record;
ReportInfo *info = record.mutable_info();
HLOGV("configNames:%zu", configNames.size());
for (auto configName : configNames) {
info->add_config_name(configName);
}
info->set_workload_cmd(workloadCmd);
protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
return true;
}
bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordLost &recordLost)
{
recordLost.DumpLog(__FUNCTION__);
recordLost_ += recordLost.data_.lost;
return true;
}
bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordComm &recordComm)
{
recordComm.DumpLog(__FUNCTION__);
HiperfRecord record;
VirtualThreadInfo *thread = record.mutable_thread();
thread->set_tid(recordComm.data_.tid);
thread->set_pid(recordComm.data_.pid);
thread->set_name(recordComm.data_.comm);
protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
return true;
}
bool ReportProtobufFileWriter::ProcessSymbolsFiles(
const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
{
uint32_t id = 0;
for (auto &symbolsFile : symbolsFiles) {
HiperfRecord record;
SymbolTableFile *message = record.mutable_file();
message->set_id(id++);
message->set_path(symbolsFile->filePath_);
for (auto &symbol : symbolsFile->GetSymbols()) {
message->add_function_name(symbol.GetName().data());
}
protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
}
return true;
}
int ReportProtobufFileReader::Read(void *buffer, int size)
{
if (protobufFileStream_->is_open()) {
try {
protobufFileStream_->read(static_cast<char *>(buffer), size);
HLOGM("readed %d bytes", size);
return size;
} catch (std::ifstream::failure &readErr) {
if (protobufFileStream_->eof()) {
HLOGW("read file %d bytes failed eof. only return %zu\n", size,
protobufFileStream_->gcount());
return protobufFileStream_->gcount();
}
HLOGW("read file %d bytes failed %s : %s\n", size, fileName_.c_str(), readErr.what());
}
} else {
printf("no file open for read (request %d bytes).\n", size);
}
return 0;
}
bool ReportProtobufFileReader::CheckFileMagic()
{
char fileMagic[sizeof(FILE_MAGIC)] = {0};
Read(fileMagic, sizeof(FILE_MAGIC) - 1);
if (memcmp(fileMagic, FILE_MAGIC, sizeof(FILE_MAGIC) - 1) != 0) {
printf("file magic is NOT correct. %s: %x\n", fileMagic, fileMagic[0]);
return false;
}
uint16_t version = 0;
Read(&version, sizeof(version));
if (version != FILE_VERSION) {
printf("file version is NOT correct.\n");
return false;
}
return true;
}
int ReportProtobufFileReader::Dump(uint32_t &recordLength, ProtobufReadBack readBack)
{
const int defaultIndent = 0;
bool readSuccess = protpbufCodedInputStream_->ReadLittleEndian32(&recordLength);
if (!readSuccess) {
printf("failed to read record length\n");
return -1;
}
if (recordLength != 0) {
PRINT_INDENT(defaultIndent, "record length:%u (%x)\n", recordLength, recordLength);
HiperfRecord record;
std::string recordBuf;
recordBuf.resize(recordLength);
if (!protpbufCodedInputStream_->ReadRaw(&recordBuf[0], recordLength)) {
printf("read record error\n");
return -1;
}
if (!record.ParseFromArray(recordBuf.data(), recordLength)) {
printf("parse format error\n");
return -1;
} else {
if (readBack == nullptr) {
PRINT_INDENT(defaultIndent, "\n");
Dump(record, defaultIndent);
} else {
readBack(record);
}
}
} else {
if (readBack == nullptr) {
printf("no more record\n");
}
return 1;
}
return 0;
}
bool ReportProtobufFileReader::Dump(const std::string fileName, ProtobufReadBack readBack)
{
fileName_ = fileName;
try {
protobufFileStream_->exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::string resolvedPath = CanonicalizeSpecPath(fileName_.c_str());
protobufFileStream_->open(resolvedPath.c_str(), std::fstream::in | std::fstream::binary);
printf("open proto buf file succeed.\n");
if (!CheckFileMagic()) {
return false;
}
protpbufInputStream_ = std::make_unique<google::protobuf::io::CopyingInputStreamAdaptor>(this);
protpbufCodedInputStream_ =
std::make_unique<google::protobuf::io::CodedInputStream>(protpbufInputStream_.get());
uint32_t recordLength = 0;
do {
int ret = Dump(recordLength, readBack);
if (ret == -1) {
return false;
} else if (ret == 1) {
break;
}
} while (recordLength != 0);
return true;
} catch (const std::fstream::failure &e) {
HLOGE("open proto buf file faild. %s\n", e.what());
}
return false;
}
bool ReportProtobufFileReader::Dump(const CallStackSample &message, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(message.GetTypeName()).c_str());
if (message.has_time()) {
PRINT_INDENT(indent + 1, "time:%" PRId64 "\n", message.time());
}
if (message.has_tid()) {
PRINT_INDENT(indent + 1, "tid:%u\n", message.tid());
}
for (int i = 0; i < message.callstackframe_size(); i++) {
PRINT_INDENT(indent + 1, "%d:\n", i);
auto &callframe = message.callstackframe(i);
if (callframe.has_symbols_vaddr()) {
PRINT_INDENT(indent + INDENT_TWO, "symbols_vaddr: 0x%" PRIx64 " \n",
callframe.symbols_vaddr());
}
if (callframe.has_symbols_file_id()) {
PRINT_INDENT(indent + INDENT_TWO, "symbols_file_id: %u\n", callframe.symbols_file_id());
}
if (callframe.has_function_name_id()) {
PRINT_INDENT(indent + INDENT_TWO, "function_name_id: %d\n", callframe.function_name_id());
}
}
if (message.has_event_count()) {
PRINT_INDENT(indent + 1, "event_count:%" PRIu64 "\n", message.event_count());
}
if (message.has_config_name_id()) {
PRINT_INDENT(indent + 1, "config_name_id:%u\n", message.config_name_id());
}
return true;
}
bool ReportProtobufFileReader::Dump(const SampleStatistic &message, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(message.GetTypeName()).c_str());
if (message.has_count()) {
PRINT_INDENT(indent + 1, "count:%" PRIu64 "\n", message.count());
}
if (message.has_lost()) {
PRINT_INDENT(indent + 1, "lost:%" PRIu64 "\n", message.lost());
}
return false;
}
bool ReportProtobufFileReader::Dump(const SymbolTableFile &message, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(message.GetTypeName()).c_str());
if (message.has_id()) {
PRINT_INDENT(indent + 1, "id: %u\n", message.id());
}
if (message.has_path()) {
PRINT_INDENT(indent + 1, "path: %s\n", std::string(message.path()).c_str());
}
for (int i = 0; i < message.function_name_size(); i++) {
PRINT_INDENT(indent + INDENT_TWO, "%d:%s\n", i, std::string(message.function_name(i)).c_str());
}
return false;
}
bool ReportProtobufFileReader::Dump(const VirtualThreadInfo &message, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(message.GetTypeName()).c_str());
if (message.has_pid()) {
PRINT_INDENT(indent + 1, "pid:%u\n", message.pid());
}
if (message.has_tid()) {
PRINT_INDENT(indent + 1, "tid:%u\n", message.tid());
}
if (message.has_pid()) {
PRINT_INDENT(indent + 1, "name:%s\n", std::string(message.name()).c_str());
}
return false;
}
bool ReportProtobufFileReader::Dump(const ReportInfo &message, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(message.GetTypeName()).c_str());
for (int i = 0; i < message.config_name_size(); i++) {
PRINT_INDENT(indent + 1, "config_name:%d:%s\n", i, std::string(message.config_name(i)).c_str());
}
if (message.has_workload_cmd()) {
PRINT_INDENT(indent + 1, "workload:%s\n", std::string(message.workload_cmd()).c_str());
}
return true;
}
bool ReportProtobufFileReader::Dump(const HiperfRecord &record, const int indent)
{
PRINT_INDENT(indent, "%s:\n", std::string(record.GetTypeName()).c_str());
if (record.has_sample()) {
return Dump(record.sample(), indent + 1);
} else if (record.has_statistic()) {
return Dump(record.statistic(), indent + 1);
} else if (record.has_file()) {
return Dump(record.file(), indent + 1);
} else if (record.has_thread()) {
return Dump(record.thread(), indent + 1);
} else if (record.has_info()) {
return Dump(record.info(), indent + 1);
} else {
printf("unknow proto buf format\n");
return false;
}
}
bool ReportProtobufFileReader::IsOpen()
{
return protobufFileStream_->is_open();
}
}
}
}