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, §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: " << ::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
|