Coverage for /Syzygy/trace/service/trace_file_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
83.6%971160.C++source

Line-by-line coverage:

   1    :  // Copyright 2013 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/trace/service/trace_file_writer.h"
  16    :  
  17    :  #include <time.h>
  18    :  
  19    :  #include "base/strings/stringprintf.h"
  20    :  #include "syzygy/common/align.h"
  21    :  #include "syzygy/common/buffer_writer.h"
  22    :  #include "syzygy/common/com_utils.h"
  23    :  #include "syzygy/common/path_util.h"
  24    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  25    :  
  26    :  namespace trace {
  27    :  namespace service {
  28    :  
  29    :  namespace {
  30    :  
  31    :  bool OpenTraceFile(const base::FilePath& file_path,
  32  E :                     base::win::ScopedHandle* file_handle) {
  33  E :    DCHECK(!file_path.empty());
  34  E :    DCHECK(file_handle != NULL);
  35    :  
  36    :    // Create a new trace file.
  37    :    base::win::ScopedHandle new_file_handle(
  38    :        ::CreateFile(file_path.value().c_str(),
  39    :                     GENERIC_READ | GENERIC_WRITE,
  40    :                     FILE_SHARE_DELETE | FILE_SHARE_READ,
  41    :                     NULL, /* lpSecurityAttributes */
  42    :                     CREATE_ALWAYS,
  43    :                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
  44  E :                     NULL /* hTemplateFile */));
  45  E :    if (!new_file_handle.IsValid()) {
  46  E :      DWORD error = ::GetLastError();
  47  E :      LOG(ERROR) << "Failed to open '" << file_path.value()
  48    :                 << "' for writing: " << ::common::LogWe(error) << ".";
  49  E :      return false;
  50    :    }
  51    :  
  52  E :    file_handle->Set(new_file_handle.Take());
  53    :  
  54  E :    return true;
  55  E :  }
  56    :  
  57  E :  bool GetBlockSize(const base::FilePath& path, size_t* block_size) {
  58    :    wchar_t volume[MAX_PATH];
  59    :  
  60  E :    if (!::GetVolumePathName(path.value().c_str(), volume, arraysize(volume))) {
  61  i :      DWORD error = ::GetLastError();
  62  i :      LOG(ERROR) << "Failed to get volume path name: " << ::common::LogWe(error)
  63    :                 << ".";
  64  i :      return false;
  65    :    }
  66    :  
  67  E :    DWORD sectors_per_cluster = 0;
  68  E :    DWORD bytes_per_sector = 0;
  69  E :    DWORD free_clusters = 0;
  70  E :    DWORD total_clusters = 0;
  71    :  
  72    :    if (!::GetDiskFreeSpace(volume, &sectors_per_cluster, &bytes_per_sector,
  73  E :                            &free_clusters, &total_clusters)) {
  74  i :      DWORD error = ::GetLastError();
  75  i :      LOG(ERROR) << "Failed to get volume info: " << ::common::LogWe(error)
  76    :                 << ".";
  77  i :      return false;
  78    :    }
  79    :  
  80  E :    *block_size = bytes_per_sector;
  81  E :    return true;
  82  E :  }
  83    :  
  84    :  }  // namespace
  85    :  
  86  E :  TraceFileWriter::TraceFileWriter() : block_size_(0) {
  87  E :  }
  88    :  
  89  E :  TraceFileWriter::~TraceFileWriter() {
  90  E :  }
  91    :  
  92    :  base::FilePath TraceFileWriter::GenerateTraceFileBaseName(
  93  E :      const ProcessInfo& process_info) {
  94    :    // We use the current time to disambiguate the trace file, so let's look
  95    :    // at the clock.
  96  E :    time_t t = time(NULL);
  97  E :    struct tm local_time = {};
  98  E :    ::localtime_s(&local_time, &t);
  99    :  
 100    :    // Construct the trace file path from the program being run, the current
 101    :    // timestamp, and the process id.
 102    :    return base::FilePath(base::StringPrintf(
 103    :        L"trace-%ls-%4d%02d%02d%02d%02d%02d-%d.bin",
 104    :        process_info.executable_path.BaseName().value().c_str(),
 105    :        1900 + local_time.tm_year,
 106    :        1 + local_time.tm_mon,
 107    :        local_time.tm_mday,
 108    :        local_time.tm_hour,
 109    :        local_time.tm_min,
 110    :        local_time.tm_sec,
 111  E :        process_info.process_id));
 112  E :  }
 113    :  
 114  E :  bool TraceFileWriter::Open(const base::FilePath& path) {
 115    :    // Open the trace file.
 116  E :    base::win::ScopedHandle temp_handle;
 117  E :    if (!OpenTraceFile(path, &temp_handle)) {
 118  E :      LOG(ERROR) << "Failed to open trace file: '"
 119    :                 << path_.value() << "'.";
 120  E :      return false;
 121    :    }
 122    :  
 123    :    // Figure out how big a physical block is on the disk.
 124    :    size_t block_size;
 125  E :    if (!GetBlockSize(path, &block_size)) {
 126  i :      LOG(ERROR) << "Failed to determine the trace file block size.";
 127  i :      return false;
 128    :    }
 129    :  
 130  E :    path_ = path;
 131  E :    handle_.Set(temp_handle.Take());
 132  E :    block_size_ = block_size;
 133    :  
 134  E :    return true;
 135  E :  }
 136    :  
 137  E :  bool TraceFileWriter::WriteHeader(const ProcessInfo& process_info) {
 138    :    // Make sure we record the path to the executable as a path with a drive
 139    :    // letter, rather than using device names.
 140  E :    base::FilePath drive_path;
 141    :    if (!::common::ConvertDevicePathToDrivePath(process_info.executable_path,
 142  E :                                                &drive_path)) {
 143  i :      return false;
 144    :    }
 145    :  
 146    :    // Allocate an initial buffer to which to write the trace file header.
 147  E :    std::vector<uint8> buffer;
 148  E :    buffer.reserve(32 * 1024);
 149    :  
 150    :    // Skip past the fixed sized portion of the header and populate the variable
 151    :    // length fields.
 152  E :    ::common::VectorBufferWriter writer(&buffer);
 153    :    if (!writer.Consume(offsetof(TraceFileHeader, blob_data)) ||
 154    :        !writer.WriteString(drive_path.value()) ||
 155    :        !writer.WriteString(process_info.command_line) ||
 156    :        !writer.Write(process_info.environment.size(),
 157  E :                      &process_info.environment[0])) {
 158  i :      return false;
 159    :    }
 160    :  
 161    :    // Go back and populate the fixed sized portion of the header.
 162  E :    TraceFileHeader* header = reinterpret_cast<TraceFileHeader*>(&buffer[0]);
 163    :    ::memcpy(&header->signature,
 164    :             &TraceFileHeader::kSignatureValue,
 165  E :             sizeof(header->signature));
 166  E :    header->server_version.lo = TRACE_VERSION_LO;
 167  E :    header->server_version.hi = TRACE_VERSION_HI;
 168  E :    header->header_size = buffer.size();
 169  E :    header->block_size = block_size_;
 170  E :    header->process_id = process_info.process_id;
 171  E :    header->module_base_address = process_info.exe_base_address;
 172  E :    header->module_size = process_info.exe_image_size;
 173  E :    header->module_checksum = process_info.exe_checksum;
 174  E :    header->module_time_date_stamp = process_info.exe_time_date_stamp;
 175  E :    header->os_version_info = process_info.os_version_info;
 176  E :    header->system_info = process_info.system_info;
 177  E :    header->memory_status = process_info.memory_status;
 178  E :    trace::common::GetClockInfo(&header->clock_info);
 179    :  
 180    :    // Align the header buffer up to the block size.
 181  E :    writer.Align(block_size_);
 182    :  
 183    :    // Commit the header page to disk.
 184  E :    DWORD bytes_written = 0;
 185    :    if (!::WriteFile(handle_.Get(), &buffer[0], buffer.size(), &bytes_written,
 186  E :                     NULL) || bytes_written != buffer.size() ) {
 187  i :      DWORD error = ::GetLastError();
 188  i :      LOG(ERROR) << "Failed writing trace file header: " << ::common::LogWe(error)
 189    :                 << ".";
 190  i :      return false;
 191    :    }
 192    :  
 193  E :    return true;
 194  E :  }
 195    :  
 196  E :  bool TraceFileWriter::WriteRecord(const void* data, size_t length) {
 197  E :    DCHECK(data != NULL);
 198    :  
 199    :    const size_t kHeaderLength =
 200  E :        sizeof(RecordPrefix) + sizeof(TraceFileSegmentHeader);
 201    :  
 202  E :    if (length < kHeaderLength) {
 203  E :      LOG(ERROR) << "Dropped buffer: too short.";
 204  E :      return false;
 205    :    }
 206    :  
 207    :    // We currently can only handle records that contain a TraceFileSegmentHeader.
 208  E :    const RecordPrefix* record = reinterpret_cast<const RecordPrefix*>(data);
 209    :    if (record->type != TraceFileSegmentHeader::kTypeId ||
 210    :        record->size != sizeof(TraceFileSegmentHeader) ||
 211    :        record->version.hi != TRACE_VERSION_HI ||
 212  E :        record->version.lo != TRACE_VERSION_LO) {
 213  E :      LOG(ERROR) << "Dropped buffer: invalid RecordPrefix.";
 214  E :      return false;
 215    :    }
 216    :  
 217    :    // Let's not trust the client to stop playing with the buffer while
 218    :    // we're writing. Whatever the length is now, is what we'll use. If the
 219    :    // segment itself is empty we simply skip writing the buffer.
 220    :    const TraceFileSegmentHeader* header =
 221  E :        reinterpret_cast<const TraceFileSegmentHeader*>(record + 1);
 222  E :    size_t segment_length = header->segment_length;
 223  E :    if (segment_length == 0) {
 224  E :      LOG(INFO) << "Not writing empty buffer.";
 225  E :      return true;
 226    :    }
 227    :  
 228    :    // Figure out the total size that we'll write to disk.
 229    :    size_t bytes_to_write = ::common::AlignUp(kHeaderLength + segment_length,
 230  E :                                              block_size_);
 231    :  
 232    :    // Ensure that the total number of bytes to write does not exceed the
 233    :    // maximum record length.
 234  E :    if (bytes_to_write > length) {
 235  E :      LOG(ERROR) << "Dropped buffer: bytes written exceeds buffer size.";
 236  E :      return false;
 237    :    }
 238    :  
 239    :    // Commit the buffer to disk.
 240    :    // TODO(rogerm): Use overlapped I/O.
 241  E :    DCHECK_LT(0u, bytes_to_write);
 242  E :    DWORD bytes_written = 0;
 243    :    if (!::WriteFile(handle_.Get(),
 244    :                     record,
 245    :                     bytes_to_write,
 246    :                     &bytes_written,
 247    :                     NULL) ||
 248  E :        bytes_written != bytes_to_write) {
 249  i :      DWORD error = ::GetLastError();
 250  i :      LOG(ERROR) << "Failed writing to '" << path_.value()
 251    :                 << "': " << ::common::LogWe(error) << ".";
 252  i :      return false;
 253    :    }
 254    :  
 255  E :    return true;
 256  E :  }
 257    :  
 258  E :  bool TraceFileWriter::Close() {
 259  E :    if (::CloseHandle(handle_.Take()) == 0) {
 260  i :      DWORD error = ::GetLastError();
 261  i :      LOG(ERROR) << "CloseHandle failed: " << ::common::LogWe(error) << ".";
 262  i :      return false;
 263    :    }
 264  E :    return true;
 265  E :  }
 266    :  
 267    :  }  // namespace service
 268    :  }  // namespace trace

Coverage information generated Thu Mar 26 16:15:41 2015.