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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
80.1%1091360.C++source

Line-by-line coverage:

   1    :  // Copyright 2012 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    :  // This file implements the TraceFileWriter and TraceFileWriterFactory classes
  16    :  // which provide an implementation and factory, respectively, for the default
  17    :  // buffer consumer used by the call trace service.
  18    :  
  19    :  #include "syzygy/trace/service/trace_file_writer.h"
  20    :  
  21    :  #include <time.h>
  22    :  
  23    :  #include "base/atomicops.h"
  24    :  #include "base/bind.h"
  25    :  #include "base/file_util.h"
  26    :  #include "base/logging.h"
  27    :  #include "base/stringprintf.h"
  28    :  #include "base/files/file_path.h"
  29    :  #include "base/memory/scoped_ptr.h"
  30    :  #include "sawbuck/common/com_utils.h"
  31    :  #include "syzygy/common/align.h"
  32    :  #include "syzygy/common/buffer_writer.h"
  33    :  #include "syzygy/common/path_util.h"
  34    :  #include "syzygy/trace/protocol/call_trace_defs.h"
  35    :  #include "syzygy/trace/service/buffer_pool.h"
  36    :  #include "syzygy/trace/service/session.h"
  37    :  #include "syzygy/trace/service/trace_file_writer_factory.h"
  38    :  
  39    :  namespace trace {
  40    :  namespace service {
  41    :  
  42    :  namespace {
  43    :  
  44    :  base::FilePath GenerateTraceFileName(const Session* session,
  45  E :                                       const base::FilePath& trace_directory) {
  46  E :    DCHECK(session != NULL);
  47  E :    DCHECK(!trace_directory.empty());
  48    :  
  49  E :    const ProcessInfo& client = session->client_info();
  50    :  
  51    :    // We use the current time to disambiguate the trace file, so let's look
  52    :    // at the clock.
  53  E :    time_t t = time(NULL);
  54  E :    struct tm local_time = {};
  55  E :    ::localtime_s(&local_time, &t);
  56    :  
  57    :    // Construct the trace file path from the program being run, the current
  58    :    // timestamp, and the process id.
  59    :    return trace_directory.Append(base::StringPrintf(
  60    :        L"trace-%ls-%4d%02d%02d%02d%02d%02d-%d.bin",
  61    :        client.executable_path.BaseName().value().c_str(),
  62    :        1900 + local_time.tm_year,
  63    :        1 + local_time.tm_mon,
  64    :        local_time.tm_mday,
  65    :        local_time.tm_hour,
  66    :        local_time.tm_min,
  67    :        local_time.tm_sec,
  68  E :        client.process_id));
  69  E :  }
  70    :  
  71    :  bool OpenTraceFile(const base::FilePath& file_path,
  72  E :                     base::win::ScopedHandle* file_handle) {
  73  E :    DCHECK(!file_path.empty());
  74  E :    DCHECK(file_handle != NULL);
  75    :  
  76    :    // Create a new trace file.
  77    :    base::win::ScopedHandle new_file_handle(
  78    :        ::CreateFile(file_path.value().c_str(),
  79    :                     GENERIC_READ | GENERIC_WRITE,
  80    :                     FILE_SHARE_DELETE | FILE_SHARE_READ,
  81    :                     NULL, /* lpSecurityAttributes */
  82    :                     CREATE_ALWAYS,
  83    :                     FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING,
  84  E :                     NULL /* hTemplateFile */));
  85  E :    if (!new_file_handle.IsValid()) {
  86  i :      DWORD error = ::GetLastError();
  87  i :      LOG(ERROR) << "Failed to open '" << file_path.value()
  88    :                 << "' for writing: " << com::LogWe(error) << ".";
  89  i :      return false;
  90    :    }
  91    :  
  92  E :    file_handle->Set(new_file_handle.Take());
  93    :  
  94  E :    return true;
  95  E :  }
  96    :  
  97  E :  bool GetBlockSize(const base::FilePath& path, size_t* block_size) {
  98    :    wchar_t volume[MAX_PATH];
  99    :  
 100  E :    if (!::GetVolumePathName(path.value().c_str(), volume, arraysize(volume))) {
 101  i :      DWORD error = ::GetLastError();
 102  i :      LOG(ERROR) << "Failed to get volume path name: " << com::LogWe(error)
 103    :                 << ".";
 104  i :      return false;
 105    :    }
 106    :  
 107  E :    DWORD sectors_per_cluster = 0;
 108  E :    DWORD bytes_per_sector = 0;
 109  E :    DWORD free_clusters = 0;
 110  E :    DWORD total_clusters = 0;
 111    :  
 112    :    if (!::GetDiskFreeSpace(volume, &sectors_per_cluster, &bytes_per_sector,
 113  E :                            &free_clusters, &total_clusters)) {
 114  i :      DWORD error = ::GetLastError();
 115  i :      LOG(ERROR) << "Failed to get volume info: " << com::LogWe(error) << ".";
 116  i :      return false;
 117    :    }
 118    :  
 119  E :    *block_size = bytes_per_sector;
 120  E :    return true;
 121  E :  }
 122    :  
 123    :  bool WriteTraceFileHeader(HANDLE file_handle,
 124    :                            const Session* session,
 125  E :                            size_t block_size) {
 126  E :    DCHECK(file_handle != INVALID_HANDLE_VALUE);
 127  E :    DCHECK(session != NULL);
 128  E :    DCHECK(block_size != 0);
 129    :  
 130  E :    const ProcessInfo& client = session->client_info();
 131    :  
 132    :    // Make sure we record the path to the executable as a path with a drive
 133    :    // letter, rather than using device names.
 134  E :    base::FilePath drive_path;
 135    :    if (!common::ConvertDevicePathToDrivePath(client.executable_path,
 136  E :                                              &drive_path)) {
 137  i :      return false;
 138    :    }
 139    :  
 140    :    // Allocate an initial buffer to which to write the trace file header.
 141  E :    std::vector<uint8> buffer;
 142  E :    buffer.reserve(32 * 1024);
 143    :  
 144    :    // Skip past the fixed sized portion of the header and populate the variable
 145    :    // length fields.
 146  E :    common::VectorBufferWriter writer(&buffer);
 147    :    if (!writer.Consume(offsetof(TraceFileHeader, blob_data)) ||
 148    :        !writer.WriteString(drive_path.value()) ||
 149    :        !writer.WriteString(client.command_line) ||
 150  E :        !writer.Write(client.environment.size(), &client.environment[0])) {
 151  i :      return false;
 152    :    }
 153    :  
 154    :    // Go back and populate the fixed sized portion of the header.
 155  E :    TraceFileHeader* header = reinterpret_cast<TraceFileHeader*>(&buffer[0]);
 156    :    ::memcpy(&header->signature,
 157    :             &TraceFileHeader::kSignatureValue,
 158  E :             sizeof(header->signature));
 159  E :    header->server_version.lo = TRACE_VERSION_LO;
 160  E :    header->server_version.hi = TRACE_VERSION_HI;
 161  E :    header->timestamp = ::GetTickCount();
 162  E :    header->process_id = client.process_id;
 163  E :    header->block_size = block_size;
 164  E :    header->module_base_address = client.exe_base_address;
 165  E :    header->module_size = client.exe_image_size;
 166  E :    header->module_checksum = client.exe_checksum;
 167  E :    header->module_time_date_stamp = client.exe_time_date_stamp;
 168  E :    header->os_version_info = client.os_version_info;
 169  E :    header->system_info = client.system_info;
 170  E :    header->memory_status = client.memory_status;
 171  E :    header->header_size = buffer.size();
 172    :  
 173    :    // Align the heeader buffer up to the block size.
 174  E :    writer.Align(block_size);
 175    :  
 176    :    // Commit the header page to disk.
 177  E :    DWORD bytes_written = 0;
 178    :    if (!::WriteFile(file_handle, &buffer[0], buffer.size(), &bytes_written,
 179  E :                     NULL) || bytes_written != buffer.size() ) {
 180  i :      DWORD error = ::GetLastError();
 181  i :      LOG(ERROR) << "Failed writing trace file header: " << com::LogWe(error)
 182    :                 << ".";
 183  i :      return false;
 184    :    }
 185    :  
 186  E :    return true;
 187  E :  }
 188    :  
 189    :  }  // namespace
 190    :  
 191    :  TraceFileWriter::TraceFileWriter(MessageLoop* message_loop,
 192    :                                   const base::FilePath& trace_directory)
 193    :      : message_loop_(message_loop),
 194    :        trace_file_path_(trace_directory),  // Will mutate to filename on Open().
 195  E :        block_size_(0) {
 196  E :    DCHECK(message_loop != NULL);
 197  E :    DCHECK(!trace_directory.empty());
 198  E :  }
 199    :  
 200  E :  bool TraceFileWriter::Open(Session* session) {
 201  E :    DCHECK(session != NULL);
 202  E :    DCHECK(!trace_file_handle_.IsValid());
 203    :  
 204  E :    if (!file_util::CreateDirectory(trace_file_path_)) {
 205  i :      LOG(ERROR) << "Failed to create trace directory: '"
 206    :                 << trace_file_path_.value() << "'.";
 207  i :      return false;
 208    :    }
 209    :  
 210    :    // Append the trace file name onto the trace file directory we stored on
 211    :    // construction.
 212  E :    trace_file_path_ = GenerateTraceFileName(session, trace_file_path_);
 213    :  
 214    :    // Open the trace file.
 215  E :    base::win::ScopedHandle temp_handle;
 216  E :    if (!OpenTraceFile(trace_file_path_, &temp_handle)) {
 217  i :      LOG(ERROR) << "Failed to open trace file: '"
 218    :                 << trace_file_path_.value() << "'.";
 219  i :      return false;
 220    :    }
 221    :  
 222    :    // Figure out how big a physical block is on the disk.
 223  E :    if (!GetBlockSize(trace_file_path_, &block_size_)) {
 224  i :      LOG(ERROR) << "Failed to determine the trace file block size.";
 225  i :      return false;
 226    :    }
 227    :  
 228    :    // Write the trace file header.
 229  E :    if (!WriteTraceFileHeader(temp_handle, session, block_size_)) {
 230  i :      LOG(ERROR) << "Failed to write trace file header.";
 231  i :      return false;
 232    :    }
 233    :  
 234  E :    trace_file_handle_.Set(temp_handle.Take());
 235    :  
 236  E :    return true;
 237  E :  }
 238    :  
 239  E :  bool TraceFileWriter::Close(Session* /* session */) {
 240  E :    return true;
 241  E :  }
 242    :  
 243  E :  bool TraceFileWriter::ConsumeBuffer(Buffer* buffer) {
 244  E :    DCHECK(buffer != NULL);
 245  E :    DCHECK(buffer->session != NULL);
 246  E :    DCHECK(message_loop_ != NULL);
 247  E :    DCHECK(trace_file_handle_.IsValid());
 248    :  
 249    :    message_loop_->PostTask(FROM_HERE,
 250    :                            base::Bind(&TraceFileWriter::WriteBuffer,
 251    :                                       this,
 252    :                                       scoped_refptr<Session>(buffer->session),
 253  E :                                       base::Unretained(buffer)));
 254    :  
 255  E :    return true;
 256  E :  }
 257    :  
 258  E :  size_t TraceFileWriter::block_size() const {
 259  E :    return block_size_;
 260  E :  }
 261    :  
 262    :  void TraceFileWriter::WriteBuffer(Session* session,
 263  E :                                    Buffer* buffer) {
 264  E :    DCHECK(session != NULL);
 265  E :    DCHECK(buffer != NULL);
 266  E :    DCHECK_EQ(session, buffer->session);
 267  E :    DCHECK_EQ(Buffer::kPendingWrite, buffer->state);
 268  E :    DCHECK_EQ(MessageLoop::current(), message_loop_);
 269  E :    DCHECK(trace_file_handle_.IsValid());
 270    :  
 271    :    // Parse the record prefix and segment header;
 272    :    volatile RecordPrefix* prefix =
 273  E :        reinterpret_cast<RecordPrefix*>(buffer->data_ptr);
 274    :    volatile TraceFileSegmentHeader* header =
 275  E :        reinterpret_cast<volatile TraceFileSegmentHeader*>(prefix + 1);
 276    :  
 277    :    // Let's not trust the client to stop playing with the buffer while
 278    :    // we're writing. Whatever the length is now, is what we'll use.
 279  E :    size_t segment_length = header->segment_length;
 280  E :    const size_t kHeaderLength = sizeof(*prefix) + sizeof(*header);
 281  E :    if (segment_length > 0) {
 282    :      size_t bytes_to_write = common::AlignUp(kHeaderLength + segment_length,
 283  E :                                              block_size_);
 284    :      if (prefix->type != TraceFileSegmentHeader::kTypeId ||
 285    :          prefix->size != sizeof(TraceFileSegmentHeader) ||
 286    :          prefix->version.hi != TRACE_VERSION_HI ||
 287  E :          prefix->version.lo != TRACE_VERSION_LO) {
 288  i :        LOG(WARNING) << "Dropped buffer: invalid segment header.";
 289  E :      } else if (bytes_to_write > buffer->buffer_size) {
 290  i :        LOG(WARNING) << "Dropped buffer: bytes written exceeds buffer size.";
 291  i :      } else {
 292    :        // Commit the buffer to disk.
 293    :        // TODO(rogerm): Use overlapped I/O.
 294  E :        DCHECK(bytes_to_write != 0);
 295  E :        DWORD bytes_written = 0;
 296    :        if (!::WriteFile(trace_file_handle_,
 297    :                         buffer->data_ptr,
 298    :                         bytes_to_write,
 299    :                         &bytes_written,
 300    :                         NULL) ||
 301  E :            bytes_written != bytes_to_write) {
 302  i :          DWORD error = ::GetLastError();
 303  i :          LOG(ERROR) << "Failed writing to '" << trace_file_path_.value()
 304    :                     << "': " << com::LogWe(error) << ".";
 305    :        }
 306    :      }
 307    :    }
 308    :  
 309    :    // It's entirely possible for this buffer to be handed out to another client
 310    :    // and for the service to be forcibly shutdown before the client has had a
 311    :    // chance to even touch the buffer. In that case, we'll end up writing the
 312    :    // buffer again. We clear the RecordPrefix and the TraceFileSegmentHeader so
 313    :    // that we'll at least see the buffer as empty and write nothing.
 314    :    ::memset(buffer->data_ptr, 0,
 315  E :             sizeof(RecordPrefix) + sizeof(TraceFileSegmentHeader));
 316    :  
 317  E :    session->RecycleBuffer(buffer);
 318  E :  }
 319    :  
 320    :  }  // namespace service
 321    :  }  // namespace trace

Coverage information generated Thu Jul 04 09:34:53 2013.