// Copyright 2016 Google Inc. All Rights Reserved. #include "call_history.h" #include #include "entry_writer.h" namespace wvcdm { namespace oemprofiler { namespace { const size_t kProfilingMemoryBudget = 64 * 1024; // 64 KB const uint8_t kOutputVersionNumber = 0x00; } CallHistory::CallHistory() : buffer_(kProfilingMemoryBudget), time_at_head_(0), time_at_tail_(0) { } void CallHistory::Write( OEM_FUNCTION fid, uint64_t start_time, uint64_t end_time, const uint8_t* meta_data, size_t meta_data_length) { // time_at_tail <= start_time <= end_time or else the subtraction // will be invalid if (start_time > end_time || start_time < time_at_tail_) { LOGE("Skipping submission. Submission has time travelled." "Start=%llu End=%llu Tail=%llu. Should be Tail<=Start<=End.", start_time, end_time, time_at_tail_); return; } EntryWriter header; header.WriteU8(fid); header.WriteVLV(start_time - time_at_tail_); header.WriteVLV(end_time - start_time); const size_t total_packet_size = header.GetSize() + meta_data_length; // The max size for a VLV is 8 bytes and the max size for a entry // writer is 32 bytes. Normally the meta data will be packed using // an entry writer so the max packet size will be 64 bytes. Since the // packet size is encoded with a single byte, the packet must first // be checked to ensure it is not too large for the cast. if (total_packet_size <= 255 && RequestSpace(total_packet_size + 1)) { buffer_.AddU8(static_cast(total_packet_size)); buffer_.AddU8s(header.GetData(), header.GetSize()); buffer_.AddU8s(meta_data, meta_data_length); time_at_tail_ = end_time; } } void CallHistory::Read(std::vector& output) const { output.push_back(kOutputVersionNumber); // write the starting time EntryWriter startingTimeWriter; if (-1 == startingTimeWriter.WriteVLV(time_at_head_)) { output.clear(); return; } for (size_t i = 0; i < startingTimeWriter.GetSize(); i++) { output.push_back(startingTimeWriter.GetData()[i]); } // write the whole circular buffer into the output buffer const size_t num_bytes = buffer_.GetUsedSpace(); for (size_t i = 0; i < num_bytes; i++) { uint8_t b; if (buffer_.PeekU8(i, &b)) { output.push_back(b); } } } bool CallHistory::RequestSpace(uint8_t num_bytes) { // check if it is possible to make enough room const size_t buffer_size = buffer_.GetFreeSpace() + buffer_.GetUsedSpace(); if (num_bytes > buffer_size) { LOGE("Requesting more space than possible (requested = %u, max = %zu)", num_bytes, buffer_size); return false; } // drop entries until we have enough space while (num_bytes > buffer_.GetFreeSpace() && DropLastEntry()); return num_bytes <= buffer_.GetFreeSpace(); } bool CallHistory::ReadNextEntryRealEndTime(uint64_t* output) { if (output == NULL) { LOGE("Cannout output to null pointer"); return false; } size_t initial_time_start_index = 2; uint64_t initial_time; const int initial_time_length = ReadVLV(initial_time_start_index, &initial_time); if (initial_time_length == -1) { LOGE("Failed to read the start time for head entry"); return false; } uint64_t delta_time; const int delta_time_length = ReadVLV( initial_time_start_index + initial_time_length, &delta_time); if (delta_time_length == -1) { LOGE("Failed to read the delta time for head entry"); return false; } *output = time_at_head_ + initial_time + delta_time; return true; } bool CallHistory::DropLastEntry() { uint8_t entry_size; uint64_t end_time; if (buffer_.PeekU8(0, &entry_size) && ReadNextEntryRealEndTime(&end_time)) { // + 1 because the entry size byte needs to be removed too if (buffer_.Remove(entry_size + 1)) { time_at_head_ = end_time; return true; } } return false; } int CallHistory::ReadVLV(size_t offset, uint64_t* output) const { uint8_t first_byte; if (buffer_.PeekU8(offset, &first_byte)) { const size_t num_bytes = (first_byte >> 5) + 1; uint64_t value = first_byte & 0x1F; for (size_t i = 1; i < num_bytes; i++) { uint8_t next_byte; if (buffer_.PeekU8(offset + i, &next_byte)) { value = value << 8 | next_byte; } else { return -1; } } *output = value; return num_bytes; } return -1; } } // namespace wvcdm } // namespace oemprofiler