1 : // Copyright 2012 Google Inc.
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 <iostream>
16 :
17 : #include "base/at_exit.h"
18 : #include "base/command_line.h"
19 : #include "base/environment.h"
20 : #include "base/file_path.h"
21 : #include "base/logging.h"
22 : #include "base/string_number_conversions.h"
23 : #include "base/string_piece.h"
24 : #include "base/string_util.h"
25 : #include "base/utf_string_conversions.h"
26 : #include "base/memory/scoped_ptr.h"
27 : #include "base/threading/thread.h"
28 : #include "sawbuck/common/com_utils.h"
29 : #include "syzygy/trace/protocol/call_trace_defs.h"
30 : #include "syzygy/trace/rpc/rpc_helpers.h"
31 : #include "syzygy/trace/service/service.h"
32 : #include "syzygy/trace/service/service_rpc_impl.h"
33 : #include "syzygy/trace/service/trace_file_writer_factory.h"
34 :
35 : namespace trace {
36 : namespace service {
37 : namespace {
38 :
39 : using ::trace::client::CreateRpcBinding;
40 : using ::trace::client::InvokeRpc;
41 :
42 : // Minimum buffer size to allow (1 MB).
43 : const int kMinBufferSize = 1024 * 1024;
44 :
45 : // Minumum number of buffers to allocate.
46 : const int kMinBuffers = 16;
47 :
48 : // A static location to which the current instance id can be saved. We
49 : // persist it here so that OnConsoleCtrl can have access to the instance
50 : // id when it is invoked on the signal handler thread.
51 : wchar_t saved_instance_id[16] = {0};
52 :
53 : // Forward declaration.
54 : bool StopService(const base::StringPiece16& instance_id);
55 :
56 : // Handler function to be called on exit signals (Ctrl-C, TERM, etc...).
57 i : BOOL WINAPI OnConsoleCtrl(DWORD ctrl_type) {
58 i : if (ctrl_type != CTRL_LOGOFF_EVENT) {
59 i : StopService(saved_instance_id);
60 i : return TRUE;
61 : }
62 i : return FALSE;
63 i : }
64 :
65 : const char* const kInstanceId = "instance-id";
66 :
67 : const char kUsage[] =
68 : "Usage: call_trace_service ACTION [OPTIONS]\n"
69 : "\n"
70 : "Actions:\n"
71 : " start Start the call trace service.\n"
72 : " stop Stop the call trace service.\n"
73 : "\n"
74 : "Options:\n"
75 : " --help Show this help message.\n"
76 : " --trace-dir=PATH The directory in which to write the trace files.\n"
77 : " --buffer-size=NUM The size (in bytes) of each buffer to allocate.\n"
78 : " --num-incremental-buffers=NUM\n"
79 : " The number of buffers by which to grow the buffer\n"
80 : " pool each time the client exhausts its available\n"
81 : " buffer space.\n"
82 : " --enable-exits Enable exit tracing (off by default).\n"
83 : " --verbose Increase the logging verbosity to also include\n"
84 : " debug-level information.\n"
85 : " --instance-id=ID A unique identifier to use for the RPC endoint.\n"
86 : " This allows multiple instances of the service to\n"
87 : " run concurently. By default, this will be the value\n"
88 : " of the SYZYGY_RPC_INSTANCE_ID environment variable,\n"
89 : " or empty.\n"
90 : "\n";
91 :
92 i : int Usage() {
93 i : std::cout << kUsage;
94 i : return 1;
95 i : }
96 :
97 E : bool GetInstanceId(const CommandLine* cmd_line, std::wstring* id) {
98 E : DCHECK(cmd_line != NULL);
99 E : DCHECK(id != NULL);
100 :
101 E : *id = cmd_line->GetSwitchValueNative(kInstanceId);
102 E : if (id->empty()) {
103 i : scoped_ptr<base::Environment> env(base::Environment::Create());
104 i : CHECK(env.get() != NULL);
105 i : std::string value;
106 i : env->GetVar(::kSyzygyRpcInstanceIdEnvVar, &value);
107 i : *id = ::UTF8ToWide(value);
108 i : }
109 :
110 E : const size_t kMaxLength = arraysize(saved_instance_id) - 1;
111 E : if (id->length() > kMaxLength) {
112 i : LOG(ERROR) << "The instance id '" << *id << "' is too long. "
113 : << "The max length is " << kMaxLength << " characters.";
114 i : return false;
115 : }
116 :
117 E : return true;
118 E : }
119 :
120 E : bool RunService(const CommandLine* cmd_line) {
121 E : DCHECK(cmd_line != NULL);
122 :
123 E : base::Thread writer_thread("trace-file-writer");
124 : if (!writer_thread.StartWithOptions(
125 E : base::Thread::Options(MessageLoop::TYPE_IO, 0))) {
126 i : LOG(ERROR) << "Failed to start call trace service writer thread.";
127 i : return 1;
128 : }
129 :
130 E : MessageLoop* message_loop = writer_thread.message_loop();
131 E : TraceFileWriterFactory trace_file_writer_factory(message_loop);
132 E : Service call_trace_service(&trace_file_writer_factory);
133 E : RpcServiceInstanceManager rpc_instance(&call_trace_service);
134 :
135 : // Get/set the instance id.
136 E : std::wstring instance_id;
137 E : if (!GetInstanceId(cmd_line, &instance_id))
138 i : return false;
139 :
140 E : call_trace_service.set_instance_id(instance_id);
141 : base::wcslcpy(saved_instance_id,
142 : instance_id.c_str(),
143 E : arraysize(saved_instance_id));
144 :
145 : // Set up the trace directory.
146 E : FilePath trace_directory(cmd_line->GetSwitchValuePath("trace-dir"));
147 E : if (trace_directory.empty())
148 E : trace_directory = FilePath(L".");
149 E : if (!trace_file_writer_factory.SetTraceFileDirectory(trace_directory))
150 i : return false;
151 :
152 : // Setup the buffer size.
153 E : std::wstring buffer_size_str(cmd_line->GetSwitchValueNative("buffer-size"));
154 E : if (!buffer_size_str.empty()) {
155 i : int num = 0;
156 i : if (!base::StringToInt(buffer_size_str, &num) || num < kMinBufferSize) {
157 i : LOG(ERROR) << "Buffer size is too small (<" << kMinBufferSize << ").";
158 i : return false;
159 : }
160 i : call_trace_service.set_buffer_size_in_bytes(num);
161 : }
162 :
163 E : if (cmd_line->HasSwitch("enable-exits")) {
164 i : call_trace_service.set_flags(TRACE_FLAG_ENTER | TRACE_FLAG_EXIT);
165 : }
166 :
167 : // Setup the number of incremental buffers
168 : std::wstring buffers_str(
169 E : cmd_line->GetSwitchValueNative("num-incremental-buffers"));
170 E : if (!buffers_str.empty()) {
171 i : int num = 0;
172 i : if (!base::StringToInt(buffers_str, &num) || num < kMinBuffers) {
173 i : LOG(ERROR) << "Number of incremental buffers is too small (<"
174 : << kMinBuffers << ").";
175 i : return false;
176 : }
177 i : call_trace_service.set_num_incremental_buffers(num);
178 : }
179 :
180 : // Setup the handler for exit signals.
181 E : if (!SetConsoleCtrlHandler(&OnConsoleCtrl, TRUE)) {
182 i : DWORD error = ::GetLastError();
183 i : LOG(ERROR) << "Failed to register shutdown handler: "
184 : << com::LogWe(error) << ".";
185 i : return false;
186 : }
187 :
188 : // Run the service in blocking mode. This will not return until the service
189 : // has been externally stopped.
190 E : call_trace_service.Start(false);
191 :
192 : // We no longer need to look out exit signals.
193 E : SetConsoleCtrlHandler(&OnConsoleCtrl, FALSE);
194 :
195 : // The call trace service will be stopped on destruction.
196 E : return true;
197 E : }
198 :
199 E : bool StopService(const base::StringPiece16& instance_id) {
200 E : std::wstring protocol;
201 E : std::wstring endpoint;
202 :
203 E : ::GetSyzygyCallTraceRpcProtocol(&protocol);
204 E : ::GetSyzygyCallTraceRpcEndpoint(instance_id, &endpoint);
205 :
206 E : LOG(INFO) << "Stopping call trace logging service instance at '"
207 : << endpoint << "' via " << protocol << '.';
208 :
209 E : handle_t binding = NULL;
210 E : if (!CreateRpcBinding(protocol, endpoint, &binding)) {
211 i : LOG(ERROR) << "Failed to connect to call trace logging service.";
212 i : return false;
213 : }
214 :
215 E : if (!InvokeRpc(CallTraceClient_Stop, binding).succeeded()) {
216 i : LOG(ERROR) << "Failed to stop call trace logging service.";
217 i : return false;
218 : }
219 :
220 : // TODO(rogerm): It would be nice to make this blocking until the
221 : // service actually shuts down. Perhaps with retries on the stop
222 : // request.
223 E : LOG(INFO) << "Call trace service shutdown has been requested.";
224 E : return true;
225 E : }
226 :
227 E : extern "C" int main(int argc, char** argv) {
228 E : base::AtExitManager at_exit_manager;
229 E : CommandLine::Init(argc, argv);
230 E : const int kVlogLevelVerbose = -2;
231 : if (!logging::InitLogging(
232 : L"",
233 : logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
234 : logging::DONT_LOCK_LOG_FILE,
235 : logging::APPEND_TO_OLD_LOG_FILE,
236 E : logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS)) {
237 i : return 1;
238 : }
239 :
240 : // TODO(rogerm): Turn on ETW logging as well.
241 :
242 E : CommandLine* cmd_line = CommandLine::ForCurrentProcess();
243 E : DCHECK(cmd_line != NULL);
244 :
245 E : if (cmd_line->HasSwitch("verbose")) {
246 E : logging::SetMinLogLevel(kVlogLevelVerbose);
247 : }
248 :
249 E : if (cmd_line->HasSwitch("help") || cmd_line->GetArgs().size() < 1) {
250 i : return Usage();
251 : }
252 :
253 E : if (LowerCaseEqualsASCII(cmd_line->GetArgs()[0], "stop")) {
254 E : std::wstring id;
255 E : return (GetInstanceId(cmd_line, &id) && StopService(id)) ? 0 : 1;
256 : }
257 :
258 E : if (LowerCaseEqualsASCII(cmd_line->GetArgs()[0], "start")) {
259 E : return RunService(cmd_line) ? 0 : 1;
260 : }
261 :
262 i : return Usage();
263 E : }
264 :
265 : } // namespace
266 : } // namespace service
267 : } // namespace trace
|