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