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