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 : #include "syzygy/trace/common/unittest_util.h"
16 :
17 : #include "base/command_line.h"
18 : #include "base/environment.h"
19 : #include "base/process_util.h"
20 : #include "base/stringprintf.h"
21 : #include "base/utf_string_conversions.h"
22 : #include "base/win/scoped_handle.h"
23 : #include "syzygy/common/align.h"
24 : #include "syzygy/common/buffer_writer.h"
25 : #include "syzygy/core/unittest_util.h"
26 : #include "syzygy/trace/protocol/call_trace_defs.h"
27 :
28 : namespace testing {
29 :
30 : CallTraceService::CallTraceService()
31 : : instance_id_(base::StringPrintf("%d", ::GetCurrentProcessId())),
32 E : service_process_(base::kNullProcessHandle) {
33 E : }
34 :
35 E : CallTraceService::~CallTraceService() {
36 E : Stop();
37 E : }
38 :
39 E : void CallTraceService::Start(const base::FilePath& trace_dir) {
40 E : ASSERT_EQ(base::kNullProcessHandle, service_process_);
41 :
42 : CommandLine service_cmd(
43 E : testing::GetExeRelativePath(L"call_trace_service.exe"));
44 E : service_cmd.AppendArg("start");
45 E : service_cmd.AppendSwitch("--verbose");
46 E : service_cmd.AppendSwitchPath("--trace-dir", trace_dir);
47 E : service_cmd.AppendSwitchASCII("--instance-id", instance_id_);
48 :
49 E : base::LaunchOptions options;
50 E : options.start_hidden = true;
51 :
52 E : std::wstring event_name;
53 : ::GetSyzygyCallTraceRpcEventName(UTF8ToUTF16(instance_id_),
54 E : &event_name);
55 : base::win::ScopedHandle event(
56 E : ::CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
57 E : ASSERT_TRUE(event.IsValid());
58 :
59 E : ASSERT_TRUE(base::LaunchProcess(service_cmd, options, &service_process_));
60 E : ASSERT_NE(base::kNullProcessHandle, service_process_);
61 :
62 : // We wait on both the "ready" handle and the process, as if the process
63 : // fails for any reason, it'll exit and its handle will become signaled.
64 E : HANDLE handles[] = { event.Get(), service_process_ };
65 : ASSERT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(arraysize(handles),
66 : handles,
67 : FALSE,
68 E : INFINITE));
69 E : }
70 :
71 E : void CallTraceService::Stop() {
72 E : if (service_process_ == base::kNullProcessHandle)
73 E : return;
74 :
75 : CommandLine service_cmd(
76 E : testing::GetExeRelativePath(L"call_trace_service.exe"));
77 E : service_cmd.AppendArg("stop");
78 E : service_cmd.AppendSwitchASCII("--instance-id", instance_id_);
79 :
80 E : base::LaunchOptions options;
81 E : options.start_hidden = true;
82 E : options.wait = true;
83 E : ASSERT_TRUE(base::LaunchProcess(service_cmd, options, NULL));
84 :
85 E : int exit_code = 0;
86 E : ASSERT_TRUE(base::WaitForExitCode(service_process_, &exit_code));
87 E : service_process_ = base::kNullProcessHandle;
88 E : }
89 :
90 E : void CallTraceService::SetEnvironment() {
91 : // The instance id needs to be in the environment to be picked up by the
92 : // client library.
93 E : scoped_ptr<base::Environment> env(base::Environment::Create());
94 E : ASSERT_FALSE(env.get() == NULL);
95 :
96 : // Get the existing value.
97 E : std::string env_var;
98 E : env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &env_var);
99 :
100 : // Prefix the existing environment variable with the instance ID we've
101 : // chosen. This allows previously intended behaviour to persist.
102 E : env_var.insert(0, ";");
103 E : env_var.insert(0, instance_id_);
104 :
105 E : ASSERT_TRUE(env->SetVar(::kSyzygyRpcInstanceIdEnvVar, env_var));
106 E : }
107 :
108 : void WriteRecord(uint64 timestamp,
109 : uint16 record_type,
110 : const void* data,
111 : size_t length,
112 E : trace::service::TraceFileWriter* writer) {
113 E : ASSERT_TRUE(data != NULL);
114 E : ASSERT_TRUE(writer != NULL);
115 :
116 E : std::vector<uint8> buffer;
117 E : ::common::VectorBufferWriter buffer_writer(&buffer);
118 :
119 E : RecordPrefix record = {};
120 E : record.timestamp = timestamp;
121 E : record.type = TraceFileSegmentHeader::kTypeId;
122 E : record.size = sizeof(TraceFileSegmentHeader);
123 E : record.version.hi = TRACE_VERSION_HI;
124 E : record.version.lo = TRACE_VERSION_LO;
125 E : ASSERT_TRUE(buffer_writer.Write(record));
126 :
127 E : TraceFileSegmentHeader header = {};
128 E : header.segment_length = sizeof(RecordPrefix) + length;
129 E : header.thread_id = ::GetCurrentThreadId();
130 E : ASSERT_TRUE(buffer_writer.Write(header));
131 :
132 E : record.type = record_type;
133 E : record.size = length;
134 E : ASSERT_TRUE(buffer_writer.Write(record));
135 :
136 : ASSERT_TRUE(buffer_writer.Write(
137 E : length, reinterpret_cast<const void*>(data)));
138 :
139 E : buffer.resize(::common::AlignUp(buffer.size(), writer->block_size()));
140 E : ASSERT_TRUE(writer->WriteRecord(buffer.data(), buffer.size()));
141 E : }
142 :
143 : } // namespace testing
|