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 : // Declares utility functions used by the call trace client and its unit
16 : // tests.
17 :
18 : #include "syzygy/trace/client/client_utils.h"
19 :
20 : #include <psapi.h>
21 : #include <memory>
22 :
23 : #include "base/environment.h"
24 : #include "base/logging.h"
25 : #include "base/files/file_util.h"
26 : #include "base/strings/string_number_conversions.h"
27 : #include "base/strings/string_split.h"
28 : #include "base/strings/utf_string_conversions.h"
29 : #include "base/win/pe_image.h"
30 : #include "syzygy/common/com_utils.h"
31 : #include "syzygy/common/path_util.h"
32 : #include "syzygy/core/file_util.h"
33 : #include "syzygy/trace/client/rpc_session.h"
34 :
35 : // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
36 : extern "C" IMAGE_DOS_HEADER __ImageBase;
37 :
38 : namespace trace {
39 : namespace client {
40 :
41 : namespace {
42 :
43 : // Loads the environment variable @p env_var and splits it at semi-colons. Each
44 : // substring is treated as a comma-separated "path,value" pair, with the first
45 : // substring being allowed to be a "value" singleton interpreted as a default
46 : // value. Looks for the presence of @p module_path in the pairs, with more
47 : // exact matches taking higher priority (highest is exact path matching,
48 : // than basename matching and finally the default value).
49 : //
50 : // Returns true if a value has been found via the environment variable, false
51 : // if no environment variable exists or no match was found. If no match is
52 : // found @p value is left unmodified.
53 : template<typename ReturnType, typename ConversionFunctor>
54 : bool GetModuleValueFromEnvVar(const char* env_var_name,
55 : const base::FilePath& module_path,
56 : const ReturnType& default_value,
57 : const ConversionFunctor& convert,
58 E : ReturnType* value) {
59 E : size_t best_score = 0;
60 E : ReturnType best_value = default_value;
61 :
62 : // Get the environment variable. If it's empty, we can return early.
63 E : std::unique_ptr<base::Environment> env(base::Environment::Create());
64 E : std::string env_var;
65 E : env->GetVar(env_var_name, &env_var);
66 E : if (env_var.empty())
67 E : return false;
68 :
69 : // Get the absolute path and the basename of the module. We will use these
70 : // for matching.
71 E : base::FilePath abs_module_path(base::MakeAbsoluteFilePath(module_path));
72 : // TODO(chrisha): Is this wise? There's all kinds of environmental trouble
73 : // that can lead to path normalization failing, and there is infact no
74 : // guarantee that an arbitrary file path can be normalized given an
75 : // arbitrary process' permissions.
76 E : CHECK(!abs_module_path.empty());
77 E : base::FilePath base_module_path = module_path.BaseName();
78 :
79 E : std::vector<std::string> pairs = base::SplitString(
80 : env_var, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
81 :
82 E : for (size_t i = 0; i < pairs.size(); ++i) {
83 E : if (pairs[i].empty())
84 i : continue;
85 :
86 E : std::vector<std::string> path_value = base::SplitString(
87 : pairs[i], ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
88 :
89 E : size_t score = 0;
90 :
91 : // Ignore malformed fields.
92 E : if (path_value.size() > 2)
93 i : continue;
94 :
95 : // Ignore entries with improperly formatted values.
96 E : ReturnType value = default_value;
97 E : if (!convert(path_value.back(), &value))
98 E : continue;
99 :
100 E : if (path_value.size() == 1) {
101 : // This is a default value specified without a path.
102 E : score = 1;
103 E : } else if (path_value.size() == 2) {
104 E : base::FilePath path(base::UTF8ToWide(path_value[0]));
105 :
106 : // Ignore improperly formatted paths.
107 E : if (path.empty())
108 i : continue;
109 :
110 E : if (base_module_path == path) {
111 : // The basename of the module matches the path.
112 E : score = 2;
113 E : } else if (abs_module_path == path) {
114 : // The full path of the module matches.
115 E : score = 3;
116 E : } else {
117 : // Due to mounting files in different locations we can often get
118 : // differing but equivalent paths to the same file. Thus, we pull out
119 : // the big guns and do a file-system level comparison to see if they
120 : // do in fact refer to the same file.
121 E : core::FilePathCompareResult result = core::CompareFilePaths(
122 : abs_module_path, path);
123 E : if (result == core::kEquivalentFilePaths)
124 i : score = 3;
125 : }
126 E : }
127 :
128 E : if (score > best_score) {
129 E : best_score = score;
130 E : best_value = value;
131 : }
132 E : }
133 :
134 E : if (best_score > 0) {
135 E : *value = best_value;
136 E : return true;
137 : }
138 :
139 E : return false;
140 E : }
141 :
142 : struct KeepAsString {
143 E : bool operator()(const std::string& s1, std::string* s2) const {
144 E : DCHECK(s2 != NULL);
145 E : *s2 = s1;
146 E : return true;
147 E : }
148 : };
149 :
150 : struct ToInt {
151 E : bool operator()(const std::string& s, int* i) const {
152 E : DCHECK(i != NULL);
153 E : if (!base::StringToInt(s, i))
154 E : return false;
155 E : return true;
156 E : }
157 : };
158 :
159 : } // namespace
160 :
161 : int ReasonToEventType(DWORD reason) {
162 : switch (reason) {
163 : case DLL_PROCESS_ATTACH:
164 : return TRACE_PROCESS_ATTACH_EVENT;
165 :
166 : case DLL_PROCESS_DETACH:
167 : return TRACE_PROCESS_DETACH_EVENT;
168 :
169 : case DLL_THREAD_ATTACH:
170 : return TRACE_THREAD_ATTACH_EVENT;
171 :
172 : case DLL_THREAD_DETACH:
173 : return TRACE_THREAD_DETACH_EVENT;
174 :
175 : default:
176 : NOTREACHED() << "Invalid reason: " << reason << ".";
177 : return -1;
178 : }
179 : }
180 :
181 E : RecordPrefix* GetRecordPrefix(void *record) {
182 E : DCHECK(record != NULL);
183 :
184 E : return reinterpret_cast<RecordPrefix*>(record) - 1;
185 E : }
186 :
187 : TraceFileSegment::TraceFileSegment()
188 E : : header(NULL),
189 E : base_ptr(NULL),
190 E : write_ptr(NULL),
191 E : end_ptr(NULL) {
192 : // Zero the RPC buffer.
193 E : memset(&buffer_info, 0, sizeof(buffer_info));
194 E : }
195 :
196 : // Returns true if there's enough space left in the given segment to write
197 : // num_bytes of raw data.
198 E : bool TraceFileSegment::CanAllocateRaw(size_t num_bytes) const {
199 E : DCHECK(write_ptr != NULL);
200 E : DCHECK(end_ptr != NULL);
201 E : DCHECK(num_bytes != 0);
202 E : if ((write_ptr + num_bytes) <= end_ptr)
203 E : return true;
204 i : return false;
205 E : }
206 :
207 : // Returns true if there's enough space left in the given segment to write
208 : // a prefixed record of length num_bytes.
209 E : bool TraceFileSegment::CanAllocate(size_t num_bytes) const {
210 E : DCHECK(num_bytes != 0);
211 E : return CanAllocateRaw(num_bytes + sizeof(RecordPrefix));
212 E : }
213 :
214 E : void FillPrefix(RecordPrefix* prefix, int type, size_t size) {
215 E : prefix->size = size;
216 E : prefix->version.hi = TRACE_VERSION_HI;
217 E : prefix->version.lo = TRACE_VERSION_LO;
218 E : prefix->type = static_cast<uint16_t>(type);
219 E : prefix->timestamp = trace::common::GetTsc();
220 E : }
221 :
222 : // Writes the segment header at the top of a segment, updating the bytes
223 : // consumed and initializing the segment header structures.
224 E : void TraceFileSegment::WriteSegmentHeader(SessionHandle session_handle) {
225 E : DCHECK(header == NULL);
226 E : DCHECK(write_ptr != NULL);
227 E : DCHECK(CanAllocate(sizeof(TraceFileSegmentHeader)));
228 :
229 : // The trace record allocation will write the record prefix and update
230 : // the number of bytes consumed within the buffer.
231 :
232 E : RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
233 E : FillPrefix(prefix,
234 : TraceFileSegmentHeader::kTypeId,
235 : sizeof(TraceFileSegmentHeader));
236 :
237 E : header = reinterpret_cast<TraceFileSegmentHeader*>(prefix + 1);
238 E : header->thread_id = ::GetCurrentThreadId();
239 E : header->segment_length = 0;
240 :
241 E : write_ptr = reinterpret_cast<uint8_t*>(header + 1);
242 E : }
243 :
244 : void* TraceFileSegment::AllocateTraceRecordImpl(int record_type,
245 E : size_t record_size) {
246 E : DCHECK(header != NULL);
247 E : DCHECK(write_ptr != NULL);
248 E : DCHECK(record_size != 0);
249 :
250 E : const size_t total_size = sizeof(RecordPrefix) + record_size;
251 :
252 E : DCHECK(CanAllocateRaw(total_size));
253 :
254 : // Clear the memory we're about to allocate. If this thread gets killed
255 : // before it can finish updating the trace record we want the allocated
256 : // record to have a somewhat consistent state.
257 E : ::memset(write_ptr, 0, total_size);
258 :
259 E : RecordPrefix* prefix = reinterpret_cast<RecordPrefix*>(write_ptr);
260 E : FillPrefix(prefix, record_type, record_size);
261 :
262 E : write_ptr += total_size;
263 E : header->segment_length += total_size;
264 :
265 E : if (!allocate_callback.is_null())
266 E : allocate_callback.Run(record_type, record_size, prefix + 1);
267 :
268 E : return prefix + 1;
269 E : }
270 :
271 E : bool GetModuleBaseAddress(void* address_in_module, void** module_base) {
272 E : DCHECK(address_in_module != NULL);
273 E : DCHECK(module_base != NULL);
274 :
275 : // Get the address of the module. We do this by querying for the allocation
276 : // that contains the address of the function we intercepted. This must lie
277 : // within the instrumented module, and be part of the single allocation in
278 : // which the image of the module lies. The base of the module will be the
279 : // base address of the allocation.
280 E : MEMORY_BASIC_INFORMATION mem_info = {};
281 E : if (::VirtualQuery(address_in_module, &mem_info, sizeof(mem_info)) == 0) {
282 i : DWORD error = ::GetLastError();
283 i : LOG(ERROR) << "VirtualQuery failed: " << ::common::LogWe(error) << ".";
284 i : return false;
285 : }
286 :
287 E : *module_base = mem_info.AllocationBase;
288 :
289 : #ifndef NDEBUG
290 E : base::win::PEImage image(*module_base);
291 E : DCHECK(image.VerifyMagic());
292 : #endif
293 :
294 E : return true;
295 E : }
296 :
297 E : bool GetModulePath(void* module_base, base::FilePath* module_path) {
298 E : DCHECK(module_base != NULL);
299 E : DCHECK(module_path != NULL);
300 :
301 : wchar_t buffer[1024];
302 : if (::GetMappedFileName(::GetCurrentProcess(), module_base, buffer,
303 E : arraysize(buffer)) == 0) {
304 i : DWORD error = ::GetLastError();
305 i : LOG(ERROR) << "GetMappedFileName failed: " << ::common::LogWe(error) << ".";
306 i : return false;
307 : }
308 :
309 E : base::FilePath device_path(buffer);
310 E : if (!::common::ConvertDevicePathToDrivePath(device_path, module_path))
311 i : return false;
312 :
313 E : return true;
314 E : }
315 :
316 E : std::string GetInstanceIdForModule(const base::FilePath& module_path) {
317 E : std::string id;
318 : // We don't care if the search is successful or not.
319 E : GetModuleValueFromEnvVar(::kSyzygyRpcInstanceIdEnvVar, module_path,
320 : id, KeepAsString(), &id);
321 E : return id;
322 E : }
323 :
324 E : std::string GetInstanceIdForThisModule() {
325 E : base::FilePath module_path;
326 E : CHECK(GetModulePath(&__ImageBase, &module_path));
327 :
328 E : std::string instance_id = GetInstanceIdForModule(module_path);
329 :
330 E : return instance_id;
331 E : }
332 :
333 E : bool IsRpcSessionMandatory(const base::FilePath& module_path) {
334 E : int value = 0;
335 E : if (!GetModuleValueFromEnvVar(kSyzygyRpcSessionMandatoryEnvVar, module_path,
336 : value, ToInt(), &value)) {
337 E : return false;
338 : }
339 :
340 E : if (value == 0)
341 E : return false;
342 :
343 : // Anything non-zero is treated as 'true'.
344 E : return true;
345 E : }
346 :
347 E : bool IsRpcSessionMandatoryForThisModule() {
348 E : base::FilePath module_path;
349 E : CHECK(GetModulePath(&__ImageBase, &module_path));
350 :
351 E : if (IsRpcSessionMandatory(module_path))
352 E : return true;
353 :
354 E : return false;
355 E : }
356 :
357 E : bool InitializeRpcSession(RpcSession* rpc_session, TraceFileSegment* segment) {
358 E : DCHECK(rpc_session != NULL);
359 :
360 E : std::string id = trace::client::GetInstanceIdForThisModule();
361 E : rpc_session->set_instance_id(base::UTF8ToWide(id));
362 E : if (rpc_session->CreateSession(segment))
363 E : return true;
364 :
365 : // If the session is not mandatory then return and indicate that we failed
366 : // to initialize properly.
367 E : if (!IsRpcSessionMandatoryForThisModule())
368 E : return false;
369 :
370 : // If you're seeing this error message it's because the process was unable
371 : // to initialize an RPC session, and the state of the
372 : // SYZYGY_RPC_SESSION_MANDATORY environment variable indicated that it was
373 : // required. Make sure the call-trace service is running with the appropriate
374 : // instance ID!
375 i : LOG(ERROR) << "RPC session is mandatory, but unable to be created.";
376 :
377 : // Dump some context regarding the decision to abort.
378 i : base::FilePath module_path;
379 i : if (GetModulePath(&__ImageBase, &module_path))
380 i : LOG(ERROR) << "Module path: " << module_path.value();
381 :
382 i : LOG(ERROR) << "RPC instance ID is \"" << id << "\".";
383 :
384 i : std::unique_ptr<base::Environment> env(base::Environment::Create());
385 i : DCHECK_NE(static_cast<base::Environment*>(nullptr), env.get());
386 i : std::string var;
387 i : if (env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &var)) {
388 i : LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is \"" << var << "\".";
389 i : } else {
390 i : LOG(ERROR) << ::kSyzygyRpcInstanceIdEnvVar << " is not set.";
391 : }
392 :
393 i : if (env->GetVar(::kSyzygyRpcSessionMandatoryEnvVar, &var)) {
394 i : LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is \"" << var
395 : << "\".";
396 i : } else {
397 i : LOG(ERROR) << ::kSyzygyRpcSessionMandatoryEnvVar << " is not set.";
398 : }
399 :
400 : // Kill this process with prejudice. We need to be heavy handed here because
401 : // we are typically running under the loader lock, and most things won't
402 : // actually convince it to stop the entire process.
403 i : ::TerminateProcess(::GetCurrentProcess(), 255);
404 :
405 : // We need this to avoid getting complaints about control paths missing a
406 : // return statement.
407 i : return false;
408 E : }
409 :
410 : } // namespace client
411 : } // namespace trace
|