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/stringprintf.h"
  20    :  #include "sawbuck/common/com_utils.h"
  21    :  #include "syzygy/common/align.h"
  22    :  #include "syzygy/common/buffer_writer.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: " << com::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: " << com::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: " << com::LogWe(error) << ".";
  76  i :      return false;
  77    :    }
  78    :  
  79  E :    *block_size = bytes_per_sector;
  80  E :    return true;
  81  E :  }
  82    :  
  83    :  }  // namespace
  84    :  
  85  E :  TraceFileWriter::TraceFileWriter() : block_size_(0) {
  86  E :  }
  87    :  
  88  E :  TraceFileWriter::~TraceFileWriter() {
  89  E :  }
  90    :  
  91    :  base::FilePath TraceFileWriter::GenerateTraceFileBaseName(
  92  E :      const ProcessInfo& process_info) {
  93    :    // We use the current time to disambiguate the trace file, so let's look
  94    :    // at the clock.
  95  E :    time_t t = time(NULL);
  96  E :    struct tm local_time = {};
  97  E :    ::localtime_s(&local_time, &t);
  98    :  
  99    :    // Construct the trace file path from the program being run, the current
 100    :    // timestamp, and the process id.
 101    :    return base::FilePath(base::StringPrintf(
 102    :        L"trace-%ls-%4d%02d%02d%02d%02d%02d-%d.bin",
 103    :        process_info.executable_path.BaseName().value().c_str(),
 104    :        1900 + local_time.tm_year,
 105    :        1 + local_time.tm_mon,
 106    :        local_time.tm_mday,
 107    :        local_time.tm_hour,
 108    :        local_time.tm_min,
 109    :        local_time.tm_sec,
 110  E :        process_info.process_id));
 111  E :  }
 112    :  
 113  E :  bool TraceFileWriter::Open(const base::FilePath& path) {
 114    :    // Open the trace file.
 115  E :    base::win::ScopedHandle temp_handle;
 116  E :    if (!OpenTraceFile(path, &temp_handle)) {
 117  E :      LOG(ERROR) << "Failed to open trace file: '"
 118    :                 << path_.value() << "'.";
 119  E :      return false;
 120    :    }
 121    :  
 122    :    // Figure out how big a physical block is on the disk.
 123    :    size_t block_size;
 124  E :    if (!GetBlockSize(path, &block_size)) {
 125  i :      LOG(ERROR) << "Failed to determine the trace file block size.";
 126  i :      return false;
 127    :    }
 128    :  
 129  E :    path_ = path;
 130  E :    handle_.Set(temp_handle.Take());
 131  E :    block_size_ = block_size;
 132    :  
 133  E :    return true;
 134  E :  }
 135    :  
 136  E :  bool TraceFileWriter::WriteHeader(const ProcessInfo& process_info) {
 137    :    // Make sure we record the path to the executable as a path with a drive
 138    :    // letter, rather than using device names.
 139  E :    base::FilePath drive_path;
 140    :    if (!::common::ConvertDevicePathToDrivePath(process_info.executable_path,
 141  E :                                                &drive_path)) {
 142  i :      return false;
 143    :    }
 144    :  
 145    :    // Allocate an initial buffer to which to write the trace file header.
 146  E :    std::vector<uint8> buffer;
 147  E :    buffer.reserve(32 * 1024);
 148    :  
 149    :    // Skip past the fixed sized portion of the header and populate the variable
 150    :    // length fields.
 151  E :    ::common::VectorBufferWriter writer(&buffer);
 152    :    if (!writer.Consume(offsetof(TraceFileHeader, blob_data)) ||
 153    :        !writer.WriteString(drive_path.value()) ||
 154    :        !writer.WriteString(process_info.command_line) ||
 155    :        !writer.Write(process_info.environment.size(),
 156  E :                      &process_info.environment[0])) {
 157  i :      return false;
 158    :    }
 159    :  
 160    :    // Go back and populate the fixed sized portion of the header.
 161  E :    TraceFileHeader* header = reinterpret_cast<TraceFileHeader*>(&buffer[0]);
 162    :    ::memcpy(&header->signature,
 163    :             &TraceFileHeader::kSignatureValue,
 164  E :             sizeof(header->signature));
 165  E :    header->server_version.lo = TRACE_VERSION_LO;
 166  E :    header->server_version.hi = TRACE_VERSION_HI;
 167  E :    header->header_size = buffer.size();
 168  E :    header->block_size = block_size_;
 169  E :    header->process_id = process_info.process_id;
 170  E :    header->module_base_address = process_info.exe_base_address;
 171  E :    header->module_size = process_info.exe_image_size;
 172  E :    header->module_checksum = process_info.exe_checksum;
 173  E :    header->module_time_date_stamp = process_info.exe_time_date_stamp;
 174  E :    header->os_version_info = process_info.os_version_info;
 175  E :    header->system_info = process_info.system_info;
 176  E :    header->memory_status = process_info.memory_status;
 177  E :    trace::common::GetClockInfo(&header->clock_info);
 178    :  
 179    :    // Align the header buffer up to the block size.
 180  E :    writer.Align(block_size_);
 181    :  
 182    :    // Commit the header page to disk.
 183  E :    DWORD bytes_written = 0;
 184    :    if (!::WriteFile(handle_.Get(), &buffer[0], buffer.size(), &bytes_written,
 185  E :                     NULL) || bytes_written != buffer.size() ) {
 186  i :      DWORD error = ::GetLastError();
 187  i :      LOG(ERROR) << "Failed writing trace file header: " << com::LogWe(error)
 188    :                 << ".";
 189  i :      return false;
 190    :    }
 191    :  
 192  E :    return true;
 193  E :  }
 194    :  
 195  E :  bool TraceFileWriter::WriteRecord(const void* data, size_t length) {
 196  E :    DCHECK(data != NULL);
 197    :  
 198    :    const size_t kHeaderLength =
 199  E :        sizeof(RecordPrefix) + sizeof(TraceFileSegmentHeader);
 200    :  
 201  E :    if (length < kHeaderLength) {
 202  E :      LOG(ERROR) << "Dropped buffer: too short.";
 203  E :      return false;
 204    :    }
 205    :  
 206    :    // We currently can only handle records that contain a TraceFileSegmentHeader.
 207  E :    const RecordPrefix* record = reinterpret_cast<const RecordPrefix*>(data);
 208    :    if (record->type != TraceFileSegmentHeader::kTypeId ||
 209    :        record->size != sizeof(TraceFileSegmentHeader) ||
 210    :        record->version.hi != TRACE_VERSION_HI ||
 211  E :        record->version.lo != TRACE_VERSION_LO) {
 212  E :      LOG(ERROR) << "Dropped buffer: invalid RecordPrefix.";
 213  E :      return false;
 214    :    }
 215    :  
 216    :    // Let's not trust the client to stop playing with the buffer while
 217    :    // we're writing. Whatever the length is now, is what we'll use. If the
 218    :    // segment itself is empty we simply skip writing the buffer.
 219    :    const TraceFileSegmentHeader* header =
 220  E :        reinterpret_cast<const TraceFileSegmentHeader*>(record + 1);
 221  E :    size_t segment_length = header->segment_length;
 222  E :    if (segment_length == 0) {
 223  E :      LOG(INFO) << "Not writing empty buffer.";
 224  E :      return true;
 225    :    }
 226    :  
 227    :    // Figure out the total size that we'll write to disk.
 228    :    size_t bytes_to_write = ::common::AlignUp(kHeaderLength + segment_length,
 229  E :                                              block_size_);
 230    :  
 231    :    // Ensure that the total number of bytes to write does not exceed the
 232    :    // maximum record length.
 233  E :    if (bytes_to_write > length) {
 234  E :      LOG(ERROR) << "Dropped buffer: bytes written exceeds buffer size.";
 235  E :      return false;
 236    :    }
 237    :  
 238    :    // Commit the buffer to disk.
 239    :    // TODO(rogerm): Use overlapped I/O.
 240  E :    DCHECK_LT(0u, bytes_to_write);
 241  E :    DWORD bytes_written = 0;
 242    :    if (!::WriteFile(handle_.Get(),
 243    :                     record,
 244    :                     bytes_to_write,
 245    :                     &bytes_written,
 246    :                     NULL) ||
 247  E :        bytes_written != bytes_to_write) {
 248  i :      DWORD error = ::GetLastError();
 249  i :      LOG(ERROR) << "Failed writing to '" << path_.value()
 250    :                 << "': " << com::LogWe(error) << ".";
 251  i :      return false;
 252    :    }
 253    :  
 254  E :    return true;
 255  E :  }
 256    :  
 257  E :  bool TraceFileWriter::Close() {
 258  E :    if (::CloseHandle(handle_.Take()) == 0) {
 259  i :      DWORD error = ::GetLastError();
 260  i :      LOG(ERROR) << "CloseHandle failed: " << com::LogWe(error) << ".";
 261  i :      return false;
 262    :    }
 263  E :    return true;
 264  E :  }
 265    :  
 266    :  }  // namespace service
 267    :  }  // namespace trace

Coverage information generated Wed Dec 11 11:34:16 2013.