blob: 1d0ca990f36b56a8c444233d43daab95904262d1 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromiumos-wide-profiling/utils.h"
#include <openssl/md5.h>
#include <sys/stat.h>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <fstream> // NOLINT(readability/streams)
#include <iomanip>
#include <sstream>
#include "base/logging.h"
#include "base/macros.h"
#include "chromiumos-wide-profiling/compat/proto.h"
#include "chromiumos-wide-profiling/file_reader.h"
namespace {
// Number of hex digits in a byte.
const int kNumHexDigitsInByte = 2;
} // namespace
namespace quipper {
static uint64_t Md5Prefix(
const unsigned char* data,
unsigned long length) { // NOLINT
uint64_t digest_prefix = 0;
unsigned char digest[MD5_DIGEST_LENGTH + 1];
MD5(data, length, digest);
// We need 64-bits / # of bits in a byte.
stringstream ss;
for (size_t i = 0; i < sizeof(uint64_t); i++)
// The setw(2) and setfill('0') calls are needed to make sure we output 2
// hex characters for every 8-bits of the hash.
ss << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<unsigned int>(digest[i]);
ss >> digest_prefix;
return digest_prefix;
}
uint64_t Md5Prefix(const string& input) {
auto data = reinterpret_cast<const unsigned char*>(input.data());
return Md5Prefix(data, input.size());
}
uint64_t Md5Prefix(const std::vector<char>& input) {
auto data = reinterpret_cast<const unsigned char*>(input.data());
return Md5Prefix(data, input.size());
}
bool FileToBuffer(const string& filename, std::vector<char>* contents) {
FileReader reader(filename);
if (!reader.IsOpen())
return false;
size_t file_size = reader.size();
contents->resize(file_size);
// Do not read anything if the file exists but is empty.
if (file_size > 0 &&
!reader.ReadData(file_size, contents->data())) {
LOG(ERROR) << "Failed to read " << file_size << " bytes from file "
<< filename << ", only read " << reader.Tell();
return false;
}
return true;
}
bool FileExists(const string& filename) {
struct stat st;
return stat(filename.c_str(), &st) == 0;
}
string RawDataToHexString(const u8* array, size_t length) {
// Convert the bytes to hex digits one at a time.
// There will be kNumHexDigitsInByte hex digits, and 1 char for NUL.
char buffer[kNumHexDigitsInByte + 1];
string result = "";
for (size_t i = 0; i < length; ++i) {
snprintf(buffer, sizeof(buffer), "%02x", array[i]);
result += buffer;
}
return result;
}
string RawDataToHexString(const string& str) {
return
RawDataToHexString(reinterpret_cast<const u8*>(str.data()), str.size());
}
bool HexStringToRawData(const string& str, u8* array, size_t length) {
const int kHexRadix = 16;
char* err;
// Loop through kNumHexDigitsInByte characters at a time (to get one byte)
// Stop when there are no more characters, or the array has been filled.
for (size_t i = 0;
(i + 1) * kNumHexDigitsInByte <= str.size() && i < length;
++i) {
string one_byte = str.substr(i * kNumHexDigitsInByte, kNumHexDigitsInByte);
array[i] = strtol(one_byte.c_str(), &err, kHexRadix);
if (*err)
return false;
}
return true;
}
bool ReadFileToData(const string& filename, std::vector<char>* data) {
std::ifstream in(filename.c_str(), std::ios::binary);
if (!in.good()) {
LOG(ERROR) << "Failed to open file " << filename;
return false;
}
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg(0, in.beg);
data->resize(length);
in.read(&(*data)[0], length);
if (!in.good()) {
LOG(ERROR) << "Error reading from file " << filename;
return false;
}
return true;
}
bool WriteDataToFile(const std::vector<char>& data, const string& filename) {
std::ofstream out(filename.c_str(), std::ios::binary);
out.seekp(0, std::ios::beg);
out.write(&data[0], data.size());
return out.good();
}
void TrimWhitespace(string* str) {
const char kWhitespaceCharacters[] = " \t\n\r";
size_t end = str->find_last_not_of(kWhitespaceCharacters);
if (end != std::string::npos) {
size_t start = str->find_first_not_of(kWhitespaceCharacters);
*str = str->substr(start, end + 1 - start);
} else {
// The string contains only whitespace.
*str = "";
}
}
void SplitString(const string& str,
char delimiter,
std::vector<string>* tokens) {
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, delimiter))
tokens->push_back(token);
}
const PerfDataProto_SampleInfo* GetSampleInfoForEvent(
const PerfDataProto_PerfEvent& event) {
switch (event.header().type()) {
case PERF_RECORD_MMAP:
case PERF_RECORD_MMAP2:
return &event.mmap_event().sample_info();
case PERF_RECORD_COMM:
return &event.comm_event().sample_info();
case PERF_RECORD_FORK:
return &event.fork_event().sample_info();
case PERF_RECORD_EXIT:
return &event.exit_event().sample_info();
case PERF_RECORD_LOST:
return &event.lost_event().sample_info();
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
return &event.throttle_event().sample_info();
case PERF_RECORD_READ:
return &event.read_event().sample_info();
}
return nullptr;
}
// Returns the correct |sample_time_ns| field of a PerfEvent.
uint64_t GetTimeFromPerfEvent(const PerfDataProto_PerfEvent& event) {
if (event.header().type() == PERF_RECORD_SAMPLE)
return event.sample_event().sample_time_ns();
const auto* sample_info = GetSampleInfoForEvent(event);
if (sample_info)
return sample_info->sample_time_ns();
return 0;
}
} // namespace quipper