1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/dbus/debug_daemon_client.h"
6
7 #include <fcntl.h>
8 #include <unistd.h>
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/files/file_path.h"
16 #include "base/location.h"
17 #include "base/memory/ref_counted_memory.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/posix/eintr_wrapper.h"
20 #include "base/strings/string_util.h"
21 #include "base/task_runner_util.h"
22 #include "base/threading/worker_pool.h"
23 #include "chromeos/dbus/pipe_reader.h"
24 #include "dbus/bus.h"
25 #include "dbus/message.h"
26 #include "dbus/object_path.h"
27 #include "dbus/object_proxy.h"
28 #include "third_party/cros_system_api/dbus/service_constants.h"
29
30 namespace {
31
32 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
EmptyStopSystemTracingCallbackBody(const scoped_refptr<base::RefCountedString> & unused_result)33 void EmptyStopSystemTracingCallbackBody(
34 const scoped_refptr<base::RefCountedString>& unused_result) {
35 }
36
37 } // namespace
38
39 namespace chromeos {
40
41 // The DebugDaemonClient implementation used in production.
42 class DebugDaemonClientImpl : public DebugDaemonClient {
43 public:
DebugDaemonClientImpl()44 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {}
45
~DebugDaemonClientImpl()46 virtual ~DebugDaemonClientImpl() {}
47
48 // DebugDaemonClient override.
GetDebugLogs(base::File file,const GetDebugLogsCallback & callback)49 virtual void GetDebugLogs(base::File file,
50 const GetDebugLogsCallback& callback) OVERRIDE {
51
52 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor;
53 file_descriptor->PutValue(file.TakePlatformFile());
54 // Punt descriptor validity check to a worker thread; on return we'll
55 // issue the D-Bus request to stop tracing and collect results.
56 base::WorkerPool::PostTaskAndReply(
57 FROM_HERE,
58 base::Bind(&dbus::FileDescriptor::CheckValidity,
59 base::Unretained(file_descriptor)),
60 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs,
61 weak_ptr_factory_.GetWeakPtr(),
62 base::Owned(file_descriptor),
63 callback),
64 false);
65 }
66
SetDebugMode(const std::string & subsystem,const SetDebugModeCallback & callback)67 virtual void SetDebugMode(const std::string& subsystem,
68 const SetDebugModeCallback& callback) OVERRIDE {
69 dbus::MethodCall method_call(debugd::kDebugdInterface,
70 debugd::kSetDebugMode);
71 dbus::MessageWriter writer(&method_call);
72 writer.AppendString(subsystem);
73 debugdaemon_proxy_->CallMethod(
74 &method_call,
75 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
76 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode,
77 weak_ptr_factory_.GetWeakPtr(),
78 callback));
79 }
80
GetRoutes(bool numeric,bool ipv6,const GetRoutesCallback & callback)81 virtual void GetRoutes(bool numeric, bool ipv6,
82 const GetRoutesCallback& callback) OVERRIDE {
83 dbus::MethodCall method_call(debugd::kDebugdInterface,
84 debugd::kGetRoutes);
85 dbus::MessageWriter writer(&method_call);
86 dbus::MessageWriter sub_writer(NULL);
87 writer.OpenArray("{sv}", &sub_writer);
88 dbus::MessageWriter elem_writer(NULL);
89 sub_writer.OpenDictEntry(&elem_writer);
90 elem_writer.AppendString("numeric");
91 elem_writer.AppendVariantOfBool(numeric);
92 sub_writer.CloseContainer(&elem_writer);
93 sub_writer.OpenDictEntry(&elem_writer);
94 elem_writer.AppendString("v6");
95 elem_writer.AppendVariantOfBool(ipv6);
96 sub_writer.CloseContainer(&elem_writer);
97 writer.CloseContainer(&sub_writer);
98 debugdaemon_proxy_->CallMethod(
99 &method_call,
100 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
101 base::Bind(&DebugDaemonClientImpl::OnGetRoutes,
102 weak_ptr_factory_.GetWeakPtr(),
103 callback));
104 }
105
GetNetworkStatus(const GetNetworkStatusCallback & callback)106 virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback)
107 OVERRIDE {
108 dbus::MethodCall method_call(debugd::kDebugdInterface,
109 debugd::kGetNetworkStatus);
110 debugdaemon_proxy_->CallMethod(
111 &method_call,
112 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
113 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus,
114 weak_ptr_factory_.GetWeakPtr(),
115 callback));
116 }
117
GetModemStatus(const GetModemStatusCallback & callback)118 virtual void GetModemStatus(const GetModemStatusCallback& callback)
119 OVERRIDE {
120 dbus::MethodCall method_call(debugd::kDebugdInterface,
121 debugd::kGetModemStatus);
122 debugdaemon_proxy_->CallMethod(
123 &method_call,
124 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
125 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus,
126 weak_ptr_factory_.GetWeakPtr(),
127 callback));
128 }
129
GetWiMaxStatus(const GetWiMaxStatusCallback & callback)130 virtual void GetWiMaxStatus(const GetWiMaxStatusCallback& callback)
131 OVERRIDE {
132 dbus::MethodCall method_call(debugd::kDebugdInterface,
133 debugd::kGetWiMaxStatus);
134 debugdaemon_proxy_->CallMethod(
135 &method_call,
136 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
137 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus,
138 weak_ptr_factory_.GetWeakPtr(),
139 callback));
140 }
141
GetNetworkInterfaces(const GetNetworkInterfacesCallback & callback)142 virtual void GetNetworkInterfaces(
143 const GetNetworkInterfacesCallback& callback) OVERRIDE {
144 dbus::MethodCall method_call(debugd::kDebugdInterface,
145 debugd::kGetInterfaces);
146 debugdaemon_proxy_->CallMethod(
147 &method_call,
148 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
149 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces,
150 weak_ptr_factory_.GetWeakPtr(),
151 callback));
152 }
153
GetPerfData(uint32_t duration,const GetPerfDataCallback & callback)154 virtual void GetPerfData(uint32_t duration,
155 const GetPerfDataCallback& callback) OVERRIDE {
156 dbus::MethodCall method_call(debugd::kDebugdInterface,
157 debugd::kGetRichPerfData);
158 dbus::MessageWriter writer(&method_call);
159 writer.AppendUint32(duration);
160
161 debugdaemon_proxy_->CallMethod(
162 &method_call,
163 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
164 base::Bind(&DebugDaemonClientImpl::OnGetPerfData,
165 weak_ptr_factory_.GetWeakPtr(),
166 callback));
167 }
168
GetScrubbedLogs(const GetLogsCallback & callback)169 virtual void GetScrubbedLogs(const GetLogsCallback& callback) OVERRIDE {
170 dbus::MethodCall method_call(debugd::kDebugdInterface,
171 debugd::kGetFeedbackLogs);
172 debugdaemon_proxy_->CallMethod(
173 &method_call,
174 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
175 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
176 weak_ptr_factory_.GetWeakPtr(),
177 callback));
178 }
179
GetAllLogs(const GetLogsCallback & callback)180 virtual void GetAllLogs(const GetLogsCallback& callback)
181 OVERRIDE {
182 dbus::MethodCall method_call(debugd::kDebugdInterface,
183 debugd::kGetAllLogs);
184 debugdaemon_proxy_->CallMethod(
185 &method_call,
186 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
187 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs,
188 weak_ptr_factory_.GetWeakPtr(),
189 callback));
190 }
191
GetUserLogFiles(const GetLogsCallback & callback)192 virtual void GetUserLogFiles(
193 const GetLogsCallback& callback) OVERRIDE {
194 dbus::MethodCall method_call(debugd::kDebugdInterface,
195 debugd::kGetUserLogFiles);
196 debugdaemon_proxy_->CallMethod(
197 &method_call,
198 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
199 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles,
200 weak_ptr_factory_.GetWeakPtr(),
201 callback));
202 }
203
StartSystemTracing()204 virtual void StartSystemTracing() OVERRIDE {
205 dbus::MethodCall method_call(
206 debugd::kDebugdInterface,
207 debugd::kSystraceStart);
208 dbus::MessageWriter writer(&method_call);
209 writer.AppendString("all"); // TODO(sleffler) parameterize category list
210
211 DVLOG(1) << "Requesting a systrace start";
212 debugdaemon_proxy_->CallMethod(
213 &method_call,
214 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
215 base::Bind(&DebugDaemonClientImpl::OnStartMethod,
216 weak_ptr_factory_.GetWeakPtr()));
217 }
218
RequestStopSystemTracing(const StopSystemTracingCallback & callback)219 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback&
220 callback) OVERRIDE {
221 if (pipe_reader_ != NULL) {
222 LOG(ERROR) << "Busy doing StopSystemTracing";
223 return false;
224 }
225
226 scoped_refptr<base::TaskRunner> task_runner =
227 base::WorkerPool::GetTaskRunner(true /* task_is_slow */);
228
229 pipe_reader_.reset(new PipeReaderForString(
230 task_runner,
231 base::Bind(&DebugDaemonClientImpl::OnIOComplete,
232 weak_ptr_factory_.GetWeakPtr())));
233
234 base::File pipe_write_end = pipe_reader_->StartIO();
235 // Create dbus::FileDescriptor on the worker thread; on return we'll
236 // issue the D-Bus request to stop tracing and collect results.
237 base::PostTaskAndReplyWithResult(
238 task_runner,
239 FROM_HERE,
240 base::Bind(
241 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing,
242 base::Passed(&pipe_write_end)),
243 base::Bind(
244 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem,
245 weak_ptr_factory_.GetWeakPtr(),
246 callback));
247 return true;
248 }
249
TestICMP(const std::string & ip_address,const TestICMPCallback & callback)250 virtual void TestICMP(const std::string& ip_address,
251 const TestICMPCallback& callback) OVERRIDE {
252 dbus::MethodCall method_call(debugd::kDebugdInterface,
253 debugd::kTestICMP);
254 dbus::MessageWriter writer(&method_call);
255 writer.AppendString(ip_address);
256 debugdaemon_proxy_->CallMethod(
257 &method_call,
258 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
259 base::Bind(&DebugDaemonClientImpl::OnTestICMP,
260 weak_ptr_factory_.GetWeakPtr(),
261 callback));
262 }
263
TestICMPWithOptions(const std::string & ip_address,const std::map<std::string,std::string> & options,const TestICMPCallback & callback)264 virtual void TestICMPWithOptions(
265 const std::string& ip_address,
266 const std::map<std::string, std::string>& options,
267 const TestICMPCallback& callback) OVERRIDE {
268 dbus::MethodCall method_call(debugd::kDebugdInterface,
269 debugd::kTestICMPWithOptions);
270 dbus::MessageWriter writer(&method_call);
271 dbus::MessageWriter sub_writer(NULL);
272 dbus::MessageWriter elem_writer(NULL);
273
274 // Write the host.
275 writer.AppendString(ip_address);
276
277 // Write the options.
278 writer.OpenArray("{ss}", &sub_writer);
279 std::map<std::string, std::string>::const_iterator it;
280 for (it = options.begin(); it != options.end(); ++it) {
281 sub_writer.OpenDictEntry(&elem_writer);
282 elem_writer.AppendString(it->first);
283 elem_writer.AppendString(it->second);
284 sub_writer.CloseContainer(&elem_writer);
285 }
286 writer.CloseContainer(&sub_writer);
287
288 // Call the function.
289 debugdaemon_proxy_->CallMethod(
290 &method_call,
291 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
292 base::Bind(&DebugDaemonClientImpl::OnTestICMP,
293 weak_ptr_factory_.GetWeakPtr(),
294 callback));
295 }
296
UploadCrashes()297 virtual void UploadCrashes() OVERRIDE {
298 dbus::MethodCall method_call(debugd::kDebugdInterface,
299 debugd::kUploadCrashes);
300 debugdaemon_proxy_->CallMethod(
301 &method_call,
302 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
303 base::Bind(&DebugDaemonClientImpl::OnStartMethod,
304 weak_ptr_factory_.GetWeakPtr()));
305 }
306
307 protected:
Init(dbus::Bus * bus)308 virtual void Init(dbus::Bus* bus) OVERRIDE {
309 debugdaemon_proxy_ =
310 bus->GetObjectProxy(debugd::kDebugdServiceName,
311 dbus::ObjectPath(debugd::kDebugdServicePath));
312 }
313
314 private:
315 // Called when a CheckValidity response is received.
OnCheckValidityGetDebugLogs(dbus::FileDescriptor * file_descriptor,const GetDebugLogsCallback & callback)316 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor* file_descriptor,
317 const GetDebugLogsCallback& callback) {
318 // Issue the dbus request to get debug logs.
319 dbus::MethodCall method_call(
320 debugd::kDebugdInterface,
321 debugd::kGetDebugLogs);
322 dbus::MessageWriter writer(&method_call);
323 writer.AppendFileDescriptor(*file_descriptor);
324
325 debugdaemon_proxy_->CallMethod(
326 &method_call,
327 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
328 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs,
329 weak_ptr_factory_.GetWeakPtr(),
330 callback));
331 }
332
333 // Called when a response for GetDebugLogs() is received.
OnGetDebugLogs(const GetDebugLogsCallback & callback,dbus::Response * response)334 void OnGetDebugLogs(const GetDebugLogsCallback& callback,
335 dbus::Response* response) {
336 if (!response) {
337 LOG(ERROR) << "Failed to get debug logs";
338 callback.Run(false);
339 return;
340 }
341 callback.Run(true);
342 }
343
344 // Called when a response for SetDebugMode() is received.
OnSetDebugMode(const SetDebugModeCallback & callback,dbus::Response * response)345 void OnSetDebugMode(const SetDebugModeCallback& callback,
346 dbus::Response* response) {
347 if (!response) {
348 LOG(ERROR) << "Failed to change debug mode";
349 callback.Run(false);
350 } else {
351 callback.Run(true);
352 }
353 }
354
OnGetRoutes(const GetRoutesCallback & callback,dbus::Response * response)355 void OnGetRoutes(const GetRoutesCallback& callback,
356 dbus::Response* response) {
357 std::vector<std::string> routes;
358 if (response) {
359 dbus::MessageReader reader(response);
360 if (reader.PopArrayOfStrings(&routes)) {
361 callback.Run(true, routes);
362 } else {
363 LOG(ERROR) << "Got non-array response from GetRoutes";
364 callback.Run(false, routes);
365 }
366 } else {
367 callback.Run(false, routes);
368 }
369 }
370
OnGetNetworkStatus(const GetNetworkStatusCallback & callback,dbus::Response * response)371 void OnGetNetworkStatus(const GetNetworkStatusCallback& callback,
372 dbus::Response* response) {
373 std::string status;
374 if (response && dbus::MessageReader(response).PopString(&status))
375 callback.Run(true, status);
376 else
377 callback.Run(false, "");
378 }
379
OnGetModemStatus(const GetModemStatusCallback & callback,dbus::Response * response)380 void OnGetModemStatus(const GetModemStatusCallback& callback,
381 dbus::Response* response) {
382 std::string status;
383 if (response && dbus::MessageReader(response).PopString(&status))
384 callback.Run(true, status);
385 else
386 callback.Run(false, "");
387 }
388
OnGetWiMaxStatus(const GetWiMaxStatusCallback & callback,dbus::Response * response)389 void OnGetWiMaxStatus(const GetWiMaxStatusCallback& callback,
390 dbus::Response* response) {
391 std::string status;
392 if (response && dbus::MessageReader(response).PopString(&status))
393 callback.Run(true, status);
394 else
395 callback.Run(false, "");
396 }
397
OnGetNetworkInterfaces(const GetNetworkInterfacesCallback & callback,dbus::Response * response)398 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback,
399 dbus::Response* response) {
400 std::string status;
401 if (response && dbus::MessageReader(response).PopString(&status))
402 callback.Run(true, status);
403 else
404 callback.Run(false, "");
405 }
406
OnGetPerfData(const GetPerfDataCallback & callback,dbus::Response * response)407 void OnGetPerfData(const GetPerfDataCallback& callback,
408 dbus::Response* response) {
409 std::vector<uint8> data;
410
411 if (!response) {
412 return;
413 }
414
415 dbus::MessageReader reader(response);
416 const uint8* buffer = NULL;
417 size_t buf_size = 0;
418 if (!reader.PopArrayOfBytes(&buffer, &buf_size))
419 return;
420
421 // TODO(asharif): Figure out a way to avoid this copy.
422 data.insert(data.end(), buffer, buffer + buf_size);
423
424 callback.Run(data);
425 }
426
OnGetAllLogs(const GetLogsCallback & callback,dbus::Response * response)427 void OnGetAllLogs(const GetLogsCallback& callback,
428 dbus::Response* response) {
429 std::map<std::string, std::string> logs;
430 bool broken = false; // did we see a broken (k,v) pair?
431 dbus::MessageReader sub_reader(NULL);
432 if (!response || !dbus::MessageReader(response).PopArray(&sub_reader)) {
433 callback.Run(false, logs);
434 return;
435 }
436 while (sub_reader.HasMoreData()) {
437 dbus::MessageReader sub_sub_reader(NULL);
438 std::string key, value;
439 if (!sub_reader.PopDictEntry(&sub_sub_reader)
440 || !sub_sub_reader.PopString(&key)
441 || !sub_sub_reader.PopString(&value)) {
442 broken = true;
443 break;
444 }
445 logs[key] = value;
446 }
447 callback.Run(!sub_reader.HasMoreData() && !broken, logs);
448 }
449
OnGetUserLogFiles(const GetLogsCallback & callback,dbus::Response * response)450 void OnGetUserLogFiles(const GetLogsCallback& callback,
451 dbus::Response* response) {
452 return OnGetAllLogs(callback, response);
453 }
454
455 // Called when a response for a simple start is received.
OnStartMethod(dbus::Response * response)456 void OnStartMethod(dbus::Response* response) {
457 if (!response) {
458 LOG(ERROR) << "Failed to request start";
459 return;
460 }
461 }
462
463 // Creates dbus::FileDescriptor from base::File.
464 static scoped_ptr<dbus::FileDescriptor>
CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end)465 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end) {
466 if (!pipe_write_end.IsValid()) {
467 LOG(ERROR) << "Cannot create pipe reader";
468 // NB: continue anyway to shutdown tracing; toss trace data
469 pipe_write_end.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
470 base::File::FLAG_OPEN | base::File::FLAG_WRITE);
471 // TODO(sleffler) if this fails AppendFileDescriptor will abort
472 }
473 scoped_ptr<dbus::FileDescriptor> file_descriptor(new dbus::FileDescriptor);
474 file_descriptor->PutValue(pipe_write_end.TakePlatformFile());
475 file_descriptor->CheckValidity();
476 return file_descriptor.Pass();
477 }
478
479 // Called when a CheckValidity response is received.
OnCreateFileDescriptorRequestStopSystem(const StopSystemTracingCallback & callback,scoped_ptr<dbus::FileDescriptor> file_descriptor)480 void OnCreateFileDescriptorRequestStopSystem(
481 const StopSystemTracingCallback& callback,
482 scoped_ptr<dbus::FileDescriptor> file_descriptor) {
483 DCHECK(file_descriptor);
484
485 // Issue the dbus request to stop system tracing
486 dbus::MethodCall method_call(
487 debugd::kDebugdInterface,
488 debugd::kSystraceStop);
489 dbus::MessageWriter writer(&method_call);
490 writer.AppendFileDescriptor(*file_descriptor);
491
492 callback_ = callback;
493
494 DVLOG(1) << "Requesting a systrace stop";
495 debugdaemon_proxy_->CallMethod(
496 &method_call,
497 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
498 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing,
499 weak_ptr_factory_.GetWeakPtr()));
500 }
501
502 // Called when a response for RequestStopSystemTracing() is received.
OnRequestStopSystemTracing(dbus::Response * response)503 void OnRequestStopSystemTracing(dbus::Response* response) {
504 if (!response) {
505 LOG(ERROR) << "Failed to request systrace stop";
506 // If debugd crashes or completes I/O before this message is processed
507 // then pipe_reader_ can be NULL, see OnIOComplete().
508 if (pipe_reader_.get())
509 pipe_reader_->OnDataReady(-1); // terminate data stream
510 }
511 // NB: requester is signaled when i/o completes
512 }
513
OnTestICMP(const TestICMPCallback & callback,dbus::Response * response)514 void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) {
515 std::string status;
516 if (response && dbus::MessageReader(response).PopString(&status))
517 callback.Run(true, status);
518 else
519 callback.Run(false, "");
520 }
521
522 // Called when pipe i/o completes; pass data on and delete the instance.
OnIOComplete()523 void OnIOComplete() {
524 std::string pipe_data;
525 pipe_reader_->GetData(&pipe_data);
526 callback_.Run(base::RefCountedString::TakeString(&pipe_data));
527 pipe_reader_.reset();
528 }
529
530 dbus::ObjectProxy* debugdaemon_proxy_;
531 scoped_ptr<PipeReaderForString> pipe_reader_;
532 StopSystemTracingCallback callback_;
533 base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_;
534
535 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl);
536 };
537
DebugDaemonClient()538 DebugDaemonClient::DebugDaemonClient() {
539 }
540
~DebugDaemonClient()541 DebugDaemonClient::~DebugDaemonClient() {
542 }
543
544 // static
545 DebugDaemonClient::StopSystemTracingCallback
EmptyStopSystemTracingCallback()546 DebugDaemonClient::EmptyStopSystemTracingCallback() {
547 return base::Bind(&EmptyStopSystemTracingCallbackBody);
548 }
549
550 // static
Create()551 DebugDaemonClient* DebugDaemonClient::Create() {
552 return new DebugDaemonClientImpl();
553 }
554
555 } // namespace chromeos
556