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/location.h" 16 #include "base/memory/ref_counted_memory.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/platform_file.h" 19 #include "base/posix/eintr_wrapper.h" 20 #include "base/strings/string_util.h" 21 #include "base/threading/worker_pool.h" 22 #include "dbus/bus.h" 23 #include "dbus/message.h" 24 #include "dbus/object_path.h" 25 #include "dbus/object_proxy.h" 26 #include "net/base/file_stream.h" 27 #include "net/base/io_buffer.h" 28 #include "net/base/net_errors.h" 29 #include "third_party/cros_system_api/dbus/service_constants.h" 30 31 namespace { 32 33 // Used in DebugDaemonClient::EmptySystemStopTracingCallback(). 34 void EmptyStopSystemTracingCallbackBody( 35 const scoped_refptr<base::RefCountedString>& unused_result) { 36 } 37 38 // Simple class to encapsulate collecting data from a pipe into a 39 // string. To use, instantiate the class, start i/o, and then delete 40 // the instance on callback. The data should be retrieved before 41 // delete and extracted or copied. 42 // 43 // TODO(sleffler) move data collection to a sub-class so this 44 // can be reused to process data as it is received 45 class PipeReader { 46 public: 47 typedef base::Callback<void(void)>IOCompleteCallback; 48 49 explicit PipeReader(IOCompleteCallback callback) 50 : io_buffer_(new net::IOBufferWithSize(4096)), 51 callback_(callback), 52 weak_ptr_factory_(this) { 53 pipe_fd_[0] = pipe_fd_[1] = -1; 54 } 55 56 virtual ~PipeReader() { 57 // Don't close pipe_fd_[0] as it's closed by data_stream_. 58 if (pipe_fd_[1] != -1) 59 if (IGNORE_EINTR(close(pipe_fd_[1])) < 0) 60 PLOG(ERROR) << "close[1]"; 61 } 62 63 // Returns descriptor for the writeable side of the pipe. 64 int GetWriteFD() { return pipe_fd_[1]; } 65 66 // Closes writeable descriptor; normally used in parent process after fork. 67 void CloseWriteFD() { 68 if (pipe_fd_[1] != -1) { 69 if (IGNORE_EINTR(close(pipe_fd_[1])) < 0) 70 PLOG(ERROR) << "close"; 71 pipe_fd_[1] = -1; 72 } 73 } 74 75 // Returns collected data. 76 std::string* data() { return &data_; } 77 78 // Starts data collection. Returns true if stream was setup correctly. 79 // On success data will automatically be accumulated into a string that 80 // can be retrieved with PipeReader::data(). To shutdown collection delete 81 // the instance and/or use PipeReader::OnDataReady(-1). 82 bool StartIO() { 83 // Use a pipe to collect data 84 const int status = HANDLE_EINTR(pipe(pipe_fd_)); 85 if (status < 0) { 86 PLOG(ERROR) << "pipe"; 87 return false; 88 } 89 base::PlatformFile data_file_ = pipe_fd_[0]; // read side 90 data_stream_.reset(new net::FileStream(data_file_, 91 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC, 92 NULL)); 93 94 // Post an initial async read to setup data collection 95 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(), 96 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr())); 97 if (rv != net::ERR_IO_PENDING) { 98 LOG(ERROR) << "Unable to post initial read"; 99 return false; 100 } 101 return true; 102 } 103 104 // Called when pipe data are available. Can also be used to shutdown 105 // data collection by passing -1 for |byte_count|. 106 void OnDataReady(int byte_count) { 107 DVLOG(1) << "OnDataReady byte_count " << byte_count; 108 if (byte_count <= 0) { 109 callback_.Run(); // signal creator to take data and delete us 110 return; 111 } 112 data_.append(io_buffer_->data(), byte_count); 113 114 // Post another read 115 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(), 116 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr())); 117 if (rv != net::ERR_IO_PENDING) { 118 LOG(ERROR) << "Unable to post another read"; 119 // TODO(sleffler) do something more intelligent? 120 } 121 } 122 123 private: 124 friend class base::RefCounted<PipeReader>; 125 126 int pipe_fd_[2]; 127 scoped_ptr<net::FileStream> data_stream_; 128 scoped_refptr<net::IOBufferWithSize> io_buffer_; 129 std::string data_; 130 IOCompleteCallback callback_; 131 132 // Note: This should remain the last member so it'll be destroyed and 133 // invalidate its weak pointers before any other members are destroyed. 134 base::WeakPtrFactory<PipeReader> weak_ptr_factory_; 135 136 DISALLOW_COPY_AND_ASSIGN(PipeReader); 137 }; 138 139 } // namespace 140 141 namespace chromeos { 142 143 // The DebugDaemonClient implementation used in production. 144 class DebugDaemonClientImpl : public DebugDaemonClient { 145 public: 146 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL), weak_ptr_factory_(this) {} 147 148 virtual ~DebugDaemonClientImpl() {} 149 150 // DebugDaemonClient override. 151 virtual void GetDebugLogs(base::PlatformFile file, 152 const GetDebugLogsCallback& callback) OVERRIDE { 153 154 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(file); 155 // Punt descriptor validity check to a worker thread; on return we'll 156 // issue the D-Bus request to stop tracing and collect results. 157 base::WorkerPool::PostTaskAndReply( 158 FROM_HERE, 159 base::Bind(&DebugDaemonClientImpl::CheckValidity, 160 file_descriptor), 161 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs, 162 weak_ptr_factory_.GetWeakPtr(), 163 base::Owned(file_descriptor), 164 callback), 165 false); 166 } 167 168 virtual void SetDebugMode(const std::string& subsystem, 169 const SetDebugModeCallback& callback) OVERRIDE { 170 dbus::MethodCall method_call(debugd::kDebugdInterface, 171 debugd::kSetDebugMode); 172 dbus::MessageWriter writer(&method_call); 173 writer.AppendString(subsystem); 174 debugdaemon_proxy_->CallMethod( 175 &method_call, 176 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 177 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode, 178 weak_ptr_factory_.GetWeakPtr(), 179 callback)); 180 } 181 182 virtual void GetRoutes(bool numeric, bool ipv6, 183 const GetRoutesCallback& callback) OVERRIDE { 184 dbus::MethodCall method_call(debugd::kDebugdInterface, 185 debugd::kGetRoutes); 186 dbus::MessageWriter writer(&method_call); 187 dbus::MessageWriter sub_writer(NULL); 188 writer.OpenArray("{sv}", &sub_writer); 189 dbus::MessageWriter elem_writer(NULL); 190 sub_writer.OpenDictEntry(&elem_writer); 191 elem_writer.AppendString("numeric"); 192 elem_writer.AppendVariantOfBool(numeric); 193 sub_writer.CloseContainer(&elem_writer); 194 sub_writer.OpenDictEntry(&elem_writer); 195 elem_writer.AppendString("v6"); 196 elem_writer.AppendVariantOfBool(ipv6); 197 sub_writer.CloseContainer(&elem_writer); 198 writer.CloseContainer(&sub_writer); 199 debugdaemon_proxy_->CallMethod( 200 &method_call, 201 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 202 base::Bind(&DebugDaemonClientImpl::OnGetRoutes, 203 weak_ptr_factory_.GetWeakPtr(), 204 callback)); 205 } 206 207 virtual void GetNetworkStatus(const GetNetworkStatusCallback& callback) 208 OVERRIDE { 209 dbus::MethodCall method_call(debugd::kDebugdInterface, 210 debugd::kGetNetworkStatus); 211 debugdaemon_proxy_->CallMethod( 212 &method_call, 213 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 214 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus, 215 weak_ptr_factory_.GetWeakPtr(), 216 callback)); 217 } 218 219 virtual void GetModemStatus(const GetModemStatusCallback& callback) 220 OVERRIDE { 221 dbus::MethodCall method_call(debugd::kDebugdInterface, 222 debugd::kGetModemStatus); 223 debugdaemon_proxy_->CallMethod( 224 &method_call, 225 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 226 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus, 227 weak_ptr_factory_.GetWeakPtr(), 228 callback)); 229 } 230 231 virtual void GetWiMaxStatus(const GetWiMaxStatusCallback& callback) 232 OVERRIDE { 233 dbus::MethodCall method_call(debugd::kDebugdInterface, 234 debugd::kGetWiMaxStatus); 235 debugdaemon_proxy_->CallMethod( 236 &method_call, 237 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 238 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus, 239 weak_ptr_factory_.GetWeakPtr(), 240 callback)); 241 } 242 243 virtual void GetNetworkInterfaces( 244 const GetNetworkInterfacesCallback& callback) OVERRIDE { 245 dbus::MethodCall method_call(debugd::kDebugdInterface, 246 debugd::kGetInterfaces); 247 debugdaemon_proxy_->CallMethod( 248 &method_call, 249 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 250 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces, 251 weak_ptr_factory_.GetWeakPtr(), 252 callback)); 253 } 254 255 virtual void GetPerfData(uint32_t duration, 256 const GetPerfDataCallback& callback) OVERRIDE { 257 dbus::MethodCall method_call(debugd::kDebugdInterface, 258 debugd::kGetRichPerfData); 259 dbus::MessageWriter writer(&method_call); 260 writer.AppendUint32(duration); 261 262 debugdaemon_proxy_->CallMethod( 263 &method_call, 264 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 265 base::Bind(&DebugDaemonClientImpl::OnGetPerfData, 266 weak_ptr_factory_.GetWeakPtr(), 267 callback)); 268 } 269 270 virtual void GetScrubbedLogs(const GetLogsCallback& callback) OVERRIDE { 271 dbus::MethodCall method_call(debugd::kDebugdInterface, 272 debugd::kGetFeedbackLogs); 273 debugdaemon_proxy_->CallMethod( 274 &method_call, 275 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 276 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs, 277 weak_ptr_factory_.GetWeakPtr(), 278 callback)); 279 } 280 281 virtual void GetAllLogs(const GetLogsCallback& callback) 282 OVERRIDE { 283 dbus::MethodCall method_call(debugd::kDebugdInterface, 284 debugd::kGetAllLogs); 285 debugdaemon_proxy_->CallMethod( 286 &method_call, 287 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 288 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs, 289 weak_ptr_factory_.GetWeakPtr(), 290 callback)); 291 } 292 293 virtual void GetUserLogFiles( 294 const GetLogsCallback& callback) OVERRIDE { 295 dbus::MethodCall method_call(debugd::kDebugdInterface, 296 debugd::kGetUserLogFiles); 297 debugdaemon_proxy_->CallMethod( 298 &method_call, 299 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 300 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles, 301 weak_ptr_factory_.GetWeakPtr(), 302 callback)); 303 } 304 305 virtual void StartSystemTracing() OVERRIDE { 306 dbus::MethodCall method_call( 307 debugd::kDebugdInterface, 308 debugd::kSystraceStart); 309 dbus::MessageWriter writer(&method_call); 310 writer.AppendString("all"); // TODO(sleffler) parameterize category list 311 312 DVLOG(1) << "Requesting a systrace start"; 313 debugdaemon_proxy_->CallMethod( 314 &method_call, 315 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 316 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing, 317 weak_ptr_factory_.GetWeakPtr())); 318 } 319 320 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback& 321 callback) OVERRIDE { 322 if (pipe_reader_ != NULL) { 323 LOG(ERROR) << "Busy doing StopSystemTracing"; 324 return false; 325 } 326 327 pipe_reader_.reset(new PipeReader( 328 base::Bind(&DebugDaemonClientImpl::OnIOComplete, 329 weak_ptr_factory_.GetWeakPtr()))); 330 int write_fd = -1; 331 if (!pipe_reader_->StartIO()) { 332 LOG(ERROR) << "Cannot create pipe reader"; 333 // NB: continue anyway to shutdown tracing; toss trace data 334 write_fd = HANDLE_EINTR(open("/dev/null", O_WRONLY)); 335 // TODO(sleffler) if this fails AppendFileDescriptor will abort 336 } else { 337 write_fd = pipe_reader_->GetWriteFD(); 338 } 339 340 dbus::FileDescriptor* file_descriptor = new dbus::FileDescriptor(write_fd); 341 // Punt descriptor validity check to a worker thread; on return we'll 342 // issue the D-Bus request to stop tracing and collect results. 343 base::WorkerPool::PostTaskAndReply( 344 FROM_HERE, 345 base::Bind(&DebugDaemonClientImpl::CheckValidity, 346 file_descriptor), 347 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem, 348 weak_ptr_factory_.GetWeakPtr(), 349 base::Owned(file_descriptor), 350 callback), 351 false); 352 353 return true; 354 } 355 356 virtual void TestICMP(const std::string& ip_address, 357 const TestICMPCallback& callback) OVERRIDE { 358 dbus::MethodCall method_call(debugd::kDebugdInterface, 359 debugd::kTestICMP); 360 dbus::MessageWriter writer(&method_call); 361 writer.AppendString(ip_address); 362 debugdaemon_proxy_->CallMethod( 363 &method_call, 364 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 365 base::Bind(&DebugDaemonClientImpl::OnTestICMP, 366 weak_ptr_factory_.GetWeakPtr(), 367 callback)); 368 } 369 370 virtual void TestICMPWithOptions( 371 const std::string& ip_address, 372 const std::map<std::string, std::string>& options, 373 const TestICMPCallback& callback) OVERRIDE { 374 dbus::MethodCall method_call(debugd::kDebugdInterface, 375 debugd::kTestICMPWithOptions); 376 dbus::MessageWriter writer(&method_call); 377 dbus::MessageWriter sub_writer(NULL); 378 dbus::MessageWriter elem_writer(NULL); 379 380 // Write the host. 381 writer.AppendString(ip_address); 382 383 // Write the options. 384 writer.OpenArray("{ss}", &sub_writer); 385 std::map<std::string, std::string>::const_iterator it; 386 for (it = options.begin(); it != options.end(); ++it) { 387 sub_writer.OpenDictEntry(&elem_writer); 388 elem_writer.AppendString(it->first); 389 elem_writer.AppendString(it->second); 390 sub_writer.CloseContainer(&elem_writer); 391 } 392 writer.CloseContainer(&sub_writer); 393 394 // Call the function. 395 debugdaemon_proxy_->CallMethod( 396 &method_call, 397 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 398 base::Bind(&DebugDaemonClientImpl::OnTestICMP, 399 weak_ptr_factory_.GetWeakPtr(), 400 callback)); 401 } 402 403 protected: 404 virtual void Init(dbus::Bus* bus) OVERRIDE { 405 debugdaemon_proxy_ = 406 bus->GetObjectProxy(debugd::kDebugdServiceName, 407 dbus::ObjectPath(debugd::kDebugdServicePath)); 408 } 409 410 private: 411 // Called to check descriptor validity on a thread where i/o is permitted. 412 static void CheckValidity(dbus::FileDescriptor* file_descriptor) { 413 file_descriptor->CheckValidity(); 414 } 415 416 // Called when a CheckValidity response is received. 417 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor* file_descriptor, 418 const GetDebugLogsCallback& callback) { 419 // Issue the dbus request to get debug logs. 420 dbus::MethodCall method_call( 421 debugd::kDebugdInterface, 422 debugd::kGetDebugLogs); 423 dbus::MessageWriter writer(&method_call); 424 writer.AppendFileDescriptor(*file_descriptor); 425 426 debugdaemon_proxy_->CallMethod( 427 &method_call, 428 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 429 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs, 430 weak_ptr_factory_.GetWeakPtr(), 431 callback)); 432 } 433 434 // Called when a response for GetDebugLogs() is received. 435 void OnGetDebugLogs(const GetDebugLogsCallback& callback, 436 dbus::Response* response) { 437 if (!response) { 438 LOG(ERROR) << "Failed to get debug logs"; 439 callback.Run(false); 440 return; 441 } 442 callback.Run(true); 443 } 444 445 // Called when a response for SetDebugMode() is received. 446 void OnSetDebugMode(const SetDebugModeCallback& callback, 447 dbus::Response* response) { 448 if (!response) { 449 LOG(ERROR) << "Failed to change debug mode"; 450 callback.Run(false); 451 } else { 452 callback.Run(true); 453 } 454 } 455 456 void OnGetRoutes(const GetRoutesCallback& callback, 457 dbus::Response* response) { 458 std::vector<std::string> routes; 459 if (response) { 460 dbus::MessageReader reader(response); 461 if (reader.PopArrayOfStrings(&routes)) { 462 callback.Run(true, routes); 463 } else { 464 LOG(ERROR) << "Got non-array response from GetRoutes"; 465 callback.Run(false, routes); 466 } 467 } else { 468 callback.Run(false, routes); 469 } 470 } 471 472 void OnGetNetworkStatus(const GetNetworkStatusCallback& callback, 473 dbus::Response* response) { 474 std::string status; 475 if (response && dbus::MessageReader(response).PopString(&status)) 476 callback.Run(true, status); 477 else 478 callback.Run(false, ""); 479 } 480 481 void OnGetModemStatus(const GetModemStatusCallback& callback, 482 dbus::Response* response) { 483 std::string status; 484 if (response && dbus::MessageReader(response).PopString(&status)) 485 callback.Run(true, status); 486 else 487 callback.Run(false, ""); 488 } 489 490 void OnGetWiMaxStatus(const GetWiMaxStatusCallback& callback, 491 dbus::Response* response) { 492 std::string status; 493 if (response && dbus::MessageReader(response).PopString(&status)) 494 callback.Run(true, status); 495 else 496 callback.Run(false, ""); 497 } 498 499 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback& callback, 500 dbus::Response* response) { 501 std::string status; 502 if (response && dbus::MessageReader(response).PopString(&status)) 503 callback.Run(true, status); 504 else 505 callback.Run(false, ""); 506 } 507 508 void OnGetPerfData(const GetPerfDataCallback& callback, 509 dbus::Response* response) { 510 std::vector<uint8> data; 511 512 if (!response) { 513 return; 514 } 515 516 dbus::MessageReader reader(response); 517 uint8* buffer = NULL; 518 size_t buf_size = 0; 519 if (!reader.PopArrayOfBytes(reinterpret_cast<uint8**>( 520 &buffer), &buf_size)) { 521 return; 522 } 523 524 // TODO(asharif): Figure out a way to avoid this copy. 525 data.insert(data.end(), buffer, buffer + buf_size); 526 527 callback.Run(data); 528 } 529 530 void OnGetAllLogs(const GetLogsCallback& callback, 531 dbus::Response* response) { 532 std::map<std::string, std::string> logs; 533 bool broken = false; // did we see a broken (k,v) pair? 534 dbus::MessageReader sub_reader(NULL); 535 if (!response || !dbus::MessageReader(response).PopArray(&sub_reader)) { 536 callback.Run(false, logs); 537 return; 538 } 539 while (sub_reader.HasMoreData()) { 540 dbus::MessageReader sub_sub_reader(NULL); 541 std::string key, value; 542 if (!sub_reader.PopDictEntry(&sub_sub_reader) 543 || !sub_sub_reader.PopString(&key) 544 || !sub_sub_reader.PopString(&value)) { 545 broken = true; 546 break; 547 } 548 logs[key] = value; 549 } 550 callback.Run(!sub_reader.HasMoreData() && !broken, logs); 551 } 552 553 void OnGetUserLogFiles(const GetLogsCallback& callback, 554 dbus::Response* response) { 555 return OnGetAllLogs(callback, response); 556 } 557 558 // Called when a response for StartSystemTracing() is received. 559 void OnStartSystemTracing(dbus::Response* response) { 560 if (!response) { 561 LOG(ERROR) << "Failed to request systrace start"; 562 return; 563 } 564 } 565 566 // Called when a CheckValidity response is received. 567 void OnCheckValidityRequestStopSystem( 568 dbus::FileDescriptor* file_descriptor, 569 const StopSystemTracingCallback& callback) { 570 // Issue the dbus request to stop system tracing 571 dbus::MethodCall method_call( 572 debugd::kDebugdInterface, 573 debugd::kSystraceStop); 574 dbus::MessageWriter writer(&method_call); 575 writer.AppendFileDescriptor(*file_descriptor); 576 577 callback_ = callback; 578 579 DVLOG(1) << "Requesting a systrace stop"; 580 debugdaemon_proxy_->CallMethod( 581 &method_call, 582 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 583 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing, 584 weak_ptr_factory_.GetWeakPtr())); 585 586 pipe_reader_->CloseWriteFD(); // close our copy of fd after send 587 } 588 589 // Called when a response for RequestStopSystemTracing() is received. 590 void OnRequestStopSystemTracing(dbus::Response* response) { 591 if (!response) { 592 LOG(ERROR) << "Failed to request systrace stop"; 593 // If debugd crashes or completes I/O before this message is processed 594 // then pipe_reader_ can be NULL, see OnIOComplete(). 595 if (pipe_reader_.get()) 596 pipe_reader_->OnDataReady(-1); // terminate data stream 597 } 598 // NB: requester is signaled when i/o completes 599 } 600 601 void OnTestICMP(const TestICMPCallback& callback, dbus::Response* response) { 602 std::string status; 603 if (response && dbus::MessageReader(response).PopString(&status)) 604 callback.Run(true, status); 605 else 606 callback.Run(false, ""); 607 } 608 609 // Called when pipe i/o completes; pass data on and delete the instance. 610 void OnIOComplete() { 611 callback_.Run(base::RefCountedString::TakeString(pipe_reader_->data())); 612 pipe_reader_.reset(); 613 } 614 615 dbus::ObjectProxy* debugdaemon_proxy_; 616 scoped_ptr<PipeReader> pipe_reader_; 617 StopSystemTracingCallback callback_; 618 base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_; 619 620 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl); 621 }; 622 623 DebugDaemonClient::DebugDaemonClient() { 624 } 625 626 DebugDaemonClient::~DebugDaemonClient() { 627 } 628 629 // static 630 DebugDaemonClient::StopSystemTracingCallback 631 DebugDaemonClient::EmptyStopSystemTracingCallback() { 632 return base::Bind(&EmptyStopSystemTracingCallbackBody); 633 } 634 635 // static 636 DebugDaemonClient* DebugDaemonClient::Create() { 637 return new DebugDaemonClientImpl(); 638 } 639 640 } // namespace chromeos 641