blob: 003c760ea4f3cab543fe430c0355fec2ef14c505 [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 <openssl/md5.h>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <zlib.h>
#include "base/basictypes.h"
#include "base/logging.h"
#include "utils.h"
namespace {
// Specify buffer size to be used to read files.
// This is allocated on the stack, so make sure it's less than 16k.
const int kFileReadSize = 1024;
// Number of hex digits in a byte.
const int kNumHexDigitsInByte = 2;
// Initial buffer size when reading compresed files.
const int kInitialBufferSizeForCompressedFiles = 4096;
} // namespace
namespace quipper {
long int GetFileSizeFromHandle(FILE* fp) {
long int position = ftell(fp);
fseek(fp, 0, SEEK_END);
long int file_size = ftell(fp);
// Restore the original file handle position.
fseek(fp, position, SEEK_SET);
return file_size;
}
event_t* CallocMemoryForEvent(size_t size) {
event_t* event = reinterpret_cast<event_t*>(calloc(1, size));
CHECK(event);
return event;
}
build_id_event* CallocMemoryForBuildID(size_t size) {
build_id_event* event = reinterpret_cast<build_id_event*>(calloc(1, size));
CHECK(event);
return event;
}
uint64 Md5Prefix(const string& input) {
uint64 digest_prefix = 0;
unsigned char digest[MD5_DIGEST_LENGTH + 1];
MD5(reinterpret_cast<const unsigned char*>(input.c_str()), input.size(),
digest);
// We need 64-bits / # of bits in a byte.
stringstream ss;
for( size_t i = 0 ; i < sizeof(uint64) ; 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;
}
bool GZFileToBuffer(const string& filename, std::vector<char>* contents) {
gzFile fp = gzopen(filename.c_str(), "rb");
if (!fp)
return false;
size_t total_bytes_read = 0;
contents->resize(kInitialBufferSizeForCompressedFiles);
while (true) {
size_t bytes_read = gzread(
fp,
&((*contents)[total_bytes_read]),
contents->size() - total_bytes_read);
total_bytes_read += bytes_read;
if (total_bytes_read != contents->size())
break;
contents->resize(contents->size() * 2);
}
contents->resize(total_bytes_read);
int error;
const char* error_string = gzerror(fp, &error);
gzclose(fp);
if (error != Z_STREAM_END && error != Z_OK) {
LOG(ERROR) << "Error while reading gzip file: " << error_string;
return false;
}
return true;
}
bool BufferToGZFile(const string& filename, const std::vector<char>& contents) {
gzFile fp;
fp = gzopen(filename.c_str(), "wb");
if (!fp)
return false;
if (!contents.empty()) {
CHECK_GT(gzwrite(fp,
&contents[0], contents.size() * sizeof(contents[0])), 0);
}
gzclose(fp);
return true;
}
bool BufferToFile(const string& filename, const std::vector<char>& contents) {
FILE* fp = fopen(filename.c_str(), "wb");
if (!fp)
return false;
// Do not write anything if |contents| contains nothing. fopen will create
// an empty file.
if (!contents.empty()) {
CHECK_GT(fwrite(&contents[0], contents.size() * sizeof(contents[0]), 1, fp),
0U);
}
fclose(fp);
return true;
}
bool FileToBuffer(const string& filename, std::vector<char>* contents) {
FILE* fp = fopen(filename.c_str(), "rb");
if (!fp)
return false;
long int file_size = quipper::GetFileSizeFromHandle(fp);
contents->resize(file_size);
// Do not read anything if the file exists but is empty.
if (file_size > 0)
CHECK_GT(fread(&(*contents)[0], file_size, 1, fp), 0U);
fclose(fp);
return true;
}
string HexToString(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;
}
bool StringToHex(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;
}
uint64 AlignSize(uint64 size, uint32 align_size) {
return ((size + align_size - 1) / align_size) * align_size;
}
// In perf data, strings are packed into the smallest number of 8-byte blocks
// possible, including the null terminator.
// e.g.
// "0123" -> 5 bytes -> packed into 8 bytes
// "0123456" -> 8 bytes -> packed into 8 bytes
// "01234567" -> 9 bytes -> packed into 16 bytes
// "0123456789abcd" -> 15 bytes -> packed into 16 bytes
// "0123456789abcde" -> 16 bytes -> packed into 16 bytes
// "0123456789abcdef" -> 17 bytes -> packed into 24 bytes
//
// Returns the size of the 8-byte-aligned memory for storing |string|.
size_t GetUint64AlignedStringLength(const string& str) {
return AlignSize(str.size() + 1, sizeof(uint64));
}
uint64 GetSampleFieldsForEventType(uint32 event_type, uint64 sample_type) {
uint64 mask = kuint64max;
switch (event_type) {
case PERF_RECORD_SAMPLE:
// IP and pid/tid fields of sample events are read as part of event_t, so
// mask away those two fields.
mask = ~(PERF_SAMPLE_IP | PERF_SAMPLE_TID);
break;
case PERF_RECORD_MMAP:
case PERF_RECORD_FORK:
case PERF_RECORD_EXIT:
case PERF_RECORD_COMM:
case PERF_RECORD_LOST:
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
mask = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
PERF_SAMPLE_CPU;
break;
case PERF_RECORD_READ:
break;
default:
LOG(FATAL) << "Unknown event type " << event_type;
}
return sample_type & mask;
}
uint64 GetPerfSampleDataOffset(const event_t& event) {
uint64 offset = kuint64max;
switch (event.header.type) {
case PERF_RECORD_SAMPLE:
offset = sizeof(event.ip);
break;
case PERF_RECORD_MMAP:
offset = sizeof(event.mmap) - sizeof(event.mmap.filename) +
GetUint64AlignedStringLength(event.mmap.filename);
break;
case PERF_RECORD_FORK:
case PERF_RECORD_EXIT:
offset = sizeof(event.fork);
break;
case PERF_RECORD_COMM:
offset = sizeof(event.comm) - sizeof(event.comm.comm) +
GetUint64AlignedStringLength(event.comm.comm);
break;
case PERF_RECORD_LOST:
offset = sizeof(event.lost);
break;
case PERF_RECORD_THROTTLE:
case PERF_RECORD_UNTHROTTLE:
offset = sizeof(event.throttle);
break;
case PERF_RECORD_READ:
offset = sizeof(event.read);
break;
default:
LOG(FATAL) << "Unknown event type " << event.header.type;
break;
}
// Make sure the offset was valid
CHECK_NE(offset, kuint64max);
CHECK_EQ(offset % sizeof(uint64), 0U);
return offset;
}
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();
}
bool RunCommandAndGetStdout(const string& command, std::vector<char>* output) {
FILE* fp = popen(command.c_str(), "r");
if (!fp)
return false;
output->clear();
char buf[kFileReadSize];
while (!feof(fp)) {
size_t size_read = fread(buf, 1, sizeof(buf), fp);
size_t prev_size = output->size();
output->resize(prev_size + size_read);
memcpy(&(*output)[prev_size], buf, size_read);
}
if (pclose(fp))
return false;
return true;
}
} // namespace quipper