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, §ors_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
|