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.
8 #include "chromeos/dbus/debug_daemon_client.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/chromeos/chromeos_version.h"
13 #include "base/memory/ref_counted_memory.h"
14 #include "base/message_loop.h"
15 #include "base/platform_file.h"
16 #include "base/posix/eintr_wrapper.h"
17 #include "base/string_util.h"
18 #include "base/threading/worker_pool.h"
20 #include "dbus/message.h"
21 #include "dbus/object_path.h"
22 #include "dbus/object_proxy.h"
23 #include "net/base/file_stream.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/net_errors.h"
26 #include "third_party/cros_system_api/dbus/service_constants.h"
30 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
31 void EmptyStopSystemTracingCallbackBody(
32 const scoped_refptr
<base::RefCountedString
>& unused_result
) {
35 // Simple class to encapsulate collecting data from a pipe into a
36 // string. To use, instantiate the class, start i/o, and then delete
37 // the instance on callback. The data should be retrieved before
38 // delete and extracted or copied.
40 // TODO(sleffler) move data collection to a sub-class so this
41 // can be reused to process data as it is received
44 typedef base::Callback
<void(void)>IOCompleteCallback
;
46 explicit PipeReader(IOCompleteCallback callback
)
48 io_buffer_(new net::IOBufferWithSize(4096)),
50 weak_ptr_factory_(this) {
51 pipe_fd_
[0] = pipe_fd_
[1] = -1;
54 virtual ~PipeReader() {
55 // Don't close pipe_fd_[0] as it's closed by data_stream_.
56 if (pipe_fd_
[1] != -1)
57 if (HANDLE_EINTR(close(pipe_fd_
[1])) < 0)
58 PLOG(ERROR
) << "close[1]";
61 // Returns descriptor for the writeable side of the pipe.
62 int GetWriteFD() { return pipe_fd_
[1]; }
64 // Closes writeable descriptor; normally used in parent process after fork.
66 if (pipe_fd_
[1] != -1) {
67 if (HANDLE_EINTR(close(pipe_fd_
[1])) < 0)
68 PLOG(ERROR
) << "close";
73 // Returns collected data.
74 std::string
* data() { return &data_
; }
76 // Starts data collection. Returns true if stream was setup correctly.
77 // On success data will automatically be accumulated into a string that
78 // can be retrieved with PipeReader::data(). To shutdown collection delete
79 // the instance and/or use PipeReader::OnDataReady(-1).
81 // Use a pipe to collect data
82 const int status
= HANDLE_EINTR(pipe(pipe_fd_
));
84 PLOG(ERROR
) << "pipe";
87 base::PlatformFile data_file_
= pipe_fd_
[0]; // read side
88 data_stream_
.reset(new net::FileStream(data_file_
,
89 base::PLATFORM_FILE_READ
| base::PLATFORM_FILE_ASYNC
,
92 // Post an initial async read to setup data collection
93 int rv
= data_stream_
->Read(io_buffer_
.get(), io_buffer_
->size(),
94 base::Bind(&PipeReader::OnDataReady
, weak_ptr_factory_
.GetWeakPtr()));
95 if (rv
!= net::ERR_IO_PENDING
) {
96 LOG(ERROR
) << "Unable to post initial read";
102 // Called when pipe data are available. Can also be used to shutdown
103 // data collection by passing -1 for |byte_count|.
104 void OnDataReady(int byte_count
) {
105 DVLOG(1) << "OnDataReady byte_count " << byte_count
;
106 if (byte_count
<= 0) {
107 callback_
.Run(); // signal creator to take data and delete us
110 data_
.append(io_buffer_
->data(), byte_count
);
113 int rv
= data_stream_
->Read(io_buffer_
.get(), io_buffer_
->size(),
114 base::Bind(&PipeReader::OnDataReady
, weak_ptr_factory_
.GetWeakPtr()));
115 if (rv
!= net::ERR_IO_PENDING
) {
116 LOG(ERROR
) << "Unable to post another read";
117 // TODO(sleffler) do something more intelligent?
122 friend class base::RefCounted
<PipeReader
>;
125 scoped_ptr
<net::FileStream
> data_stream_
;
126 scoped_refptr
<net::IOBufferWithSize
> io_buffer_
;
128 IOCompleteCallback callback_
;
130 // Note: This should remain the last member so it'll be destroyed and
131 // invalidate its weak pointers before any other members are destroyed.
132 base::WeakPtrFactory
<PipeReader
> weak_ptr_factory_
;
134 DISALLOW_COPY_AND_ASSIGN(PipeReader
);
141 // The DebugDaemonClient implementation used in production.
142 class DebugDaemonClientImpl
: public DebugDaemonClient
{
144 explicit DebugDaemonClientImpl(dbus::Bus
* bus
)
145 : debugdaemon_proxy_(NULL
),
147 weak_ptr_factory_(this) {
148 debugdaemon_proxy_
= bus
->GetObjectProxy(
149 debugd::kDebugdServiceName
,
150 dbus::ObjectPath(debugd::kDebugdServicePath
));
153 virtual ~DebugDaemonClientImpl() {}
155 // DebugDaemonClient override.
156 virtual void GetDebugLogs(base::PlatformFile file
,
157 const GetDebugLogsCallback
& callback
) OVERRIDE
{
159 dbus::FileDescriptor
* file_descriptor
= new dbus::FileDescriptor(file
);
160 // Punt descriptor validity check to a worker thread; on return we'll
161 // issue the D-Bus request to stop tracing and collect results.
162 base::WorkerPool::PostTaskAndReply(
164 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
166 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
167 weak_ptr_factory_
.GetWeakPtr(),
168 base::Owned(file_descriptor
),
173 virtual void SetDebugMode(const std::string
& subsystem
,
174 const SetDebugModeCallback
& callback
) OVERRIDE
{
175 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
176 debugd::kSetDebugMode
);
177 dbus::MessageWriter
writer(&method_call
);
178 writer
.AppendString(subsystem
);
179 debugdaemon_proxy_
->CallMethod(
181 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
182 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode
,
183 weak_ptr_factory_
.GetWeakPtr(),
187 virtual void GetRoutes(bool numeric
, bool ipv6
,
188 const GetRoutesCallback
& callback
) OVERRIDE
{
189 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
191 dbus::MessageWriter
writer(&method_call
);
192 dbus::MessageWriter
sub_writer(NULL
);
193 writer
.OpenArray("{sv}", &sub_writer
);
194 dbus::MessageWriter
elem_writer(NULL
);
195 sub_writer
.OpenDictEntry(&elem_writer
);
196 elem_writer
.AppendString("numeric");
197 elem_writer
.AppendVariantOfBool(numeric
);
198 sub_writer
.CloseContainer(&elem_writer
);
199 sub_writer
.OpenDictEntry(&elem_writer
);
200 elem_writer
.AppendString("v6");
201 elem_writer
.AppendVariantOfBool(ipv6
);
202 sub_writer
.CloseContainer(&elem_writer
);
203 writer
.CloseContainer(&sub_writer
);
204 debugdaemon_proxy_
->CallMethod(
206 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
207 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
208 weak_ptr_factory_
.GetWeakPtr(),
212 virtual void GetNetworkStatus(const GetNetworkStatusCallback
& callback
)
214 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
215 debugd::kGetNetworkStatus
);
216 debugdaemon_proxy_
->CallMethod(
218 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
219 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus
,
220 weak_ptr_factory_
.GetWeakPtr(),
224 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
226 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
227 debugd::kGetModemStatus
);
228 debugdaemon_proxy_
->CallMethod(
230 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
231 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
232 weak_ptr_factory_
.GetWeakPtr(),
236 virtual void GetNetworkInterfaces(
237 const GetNetworkInterfacesCallback
& callback
) OVERRIDE
{
238 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
239 debugd::kGetInterfaces
);
240 debugdaemon_proxy_
->CallMethod(
242 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
243 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
244 weak_ptr_factory_
.GetWeakPtr(),
248 virtual void GetPerfData(uint32_t duration
,
249 const GetPerfDataCallback
& callback
) OVERRIDE
{
250 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
251 debugd::kGetPerfData
);
252 dbus::MessageWriter
writer(&method_call
);
253 writer
.AppendUint32(duration
);
255 debugdaemon_proxy_
->CallMethod(
257 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
258 base::Bind(&DebugDaemonClientImpl::OnGetPerfData
,
259 weak_ptr_factory_
.GetWeakPtr(),
263 virtual void GetAllLogs(const GetLogsCallback
& callback
)
265 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
266 debugd::kGetAllLogs
);
267 debugdaemon_proxy_
->CallMethod(
269 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
270 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
271 weak_ptr_factory_
.GetWeakPtr(),
275 virtual void GetUserLogFiles(
276 const GetLogsCallback
& callback
) OVERRIDE
{
277 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
278 debugd::kGetUserLogFiles
);
279 debugdaemon_proxy_
->CallMethod(
281 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
282 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
283 weak_ptr_factory_
.GetWeakPtr(),
287 virtual void StartSystemTracing() OVERRIDE
{
288 dbus::MethodCall
method_call(
289 debugd::kDebugdInterface
,
290 debugd::kSystraceStart
);
291 dbus::MessageWriter
writer(&method_call
);
292 writer
.AppendString("all"); // TODO(sleffler) parameterize category list
294 DVLOG(1) << "Requesting a systrace start";
295 debugdaemon_proxy_
->CallMethod(
297 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
298 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing
,
299 weak_ptr_factory_
.GetWeakPtr()));
302 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
304 if (pipe_reader_
!= NULL
) {
305 LOG(ERROR
) << "Busy doing StopSystemTracing";
309 pipe_reader_
.reset(new PipeReader(
310 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
311 weak_ptr_factory_
.GetWeakPtr())));
313 if (!pipe_reader_
->StartIO()) {
314 LOG(ERROR
) << "Cannot create pipe reader";
315 // NB: continue anyway to shutdown tracing; toss trace data
316 write_fd
= HANDLE_EINTR(open("/dev/null", O_WRONLY
));
317 // TODO(sleffler) if this fails AppendFileDescriptor will abort
319 write_fd
= pipe_reader_
->GetWriteFD();
322 dbus::FileDescriptor
* file_descriptor
= new dbus::FileDescriptor(write_fd
);
323 // Punt descriptor validity check to a worker thread; on return we'll
324 // issue the D-Bus request to stop tracing and collect results.
325 base::WorkerPool::PostTaskAndReply(
327 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
329 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem
,
330 weak_ptr_factory_
.GetWeakPtr(),
331 base::Owned(file_descriptor
),
338 virtual void TestICMP(const std::string
& ip_address
,
339 const TestICMPCallback
& callback
) OVERRIDE
{
340 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
342 dbus::MessageWriter
writer(&method_call
);
343 writer
.AppendString(ip_address
);
344 debugdaemon_proxy_
->CallMethod(
346 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
347 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
348 weak_ptr_factory_
.GetWeakPtr(),
353 // Called to check descriptor validity on a thread where i/o is permitted.
354 static void CheckValidity(dbus::FileDescriptor
* file_descriptor
) {
355 file_descriptor
->CheckValidity();
358 // Called when a CheckValidity response is received.
359 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor
* file_descriptor
,
360 const GetDebugLogsCallback
& callback
) {
361 // Issue the dbus request to get debug logs.
362 dbus::MethodCall
method_call(
363 debugd::kDebugdInterface
,
364 debugd::kGetDebugLogs
);
365 dbus::MessageWriter
writer(&method_call
);
366 writer
.AppendFileDescriptor(*file_descriptor
);
368 debugdaemon_proxy_
->CallMethod(
370 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
371 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
372 weak_ptr_factory_
.GetWeakPtr(),
376 // Called when a response for GetDebugLogs() is received.
377 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
378 dbus::Response
* response
) {
380 LOG(ERROR
) << "Failed to get debug logs";
387 // Called when a response for SetDebugMode() is received.
388 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
389 dbus::Response
* response
) {
391 LOG(ERROR
) << "Failed to change debug mode";
398 void OnGetRoutes(const GetRoutesCallback
& callback
,
399 dbus::Response
* response
) {
400 std::vector
<std::string
> routes
;
402 dbus::MessageReader
reader(response
);
403 if (reader
.PopArrayOfStrings(&routes
)) {
404 callback
.Run(true, routes
);
406 LOG(ERROR
) << "Got non-array response from GetRoutes";
407 callback
.Run(false, routes
);
410 callback
.Run(false, routes
);
414 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
415 dbus::Response
* response
) {
417 if (response
&& dbus::MessageReader(response
).PopString(&status
))
418 callback
.Run(true, status
);
420 callback
.Run(false, "");
423 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
424 dbus::Response
* response
) {
426 if (response
&& dbus::MessageReader(response
).PopString(&status
))
427 callback
.Run(true, status
);
429 callback
.Run(false, "");
432 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
433 dbus::Response
* response
) {
435 if (response
&& dbus::MessageReader(response
).PopString(&status
))
436 callback
.Run(true, status
);
438 callback
.Run(false, "");
441 void OnGetPerfData(const GetPerfDataCallback
& callback
,
442 dbus::Response
* response
) {
443 std::vector
<uint8
> data
;
449 dbus::MessageReader
reader(response
);
450 uint8
* buffer
= NULL
;
452 if (!reader
.PopArrayOfBytes(reinterpret_cast<uint8
**>(
453 &buffer
), &buf_size
)) {
457 // TODO(asharif): Figure out a way to avoid this copy.
458 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
463 void OnGetAllLogs(const GetLogsCallback
& callback
,
464 dbus::Response
* response
) {
465 std::map
<std::string
, std::string
> logs
;
466 bool broken
= false; // did we see a broken (k,v) pair?
467 dbus::MessageReader
sub_reader(NULL
);
468 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
469 callback
.Run(false, logs
);
472 while (sub_reader
.HasMoreData()) {
473 dbus::MessageReader
sub_sub_reader(NULL
);
474 std::string key
, value
;
475 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
476 || !sub_sub_reader
.PopString(&key
)
477 || !sub_sub_reader
.PopString(&value
)) {
483 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
486 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
487 dbus::Response
* response
) {
488 return OnGetAllLogs(callback
, response
);
491 // Called when a response for StartSystemTracing() is received.
492 void OnStartSystemTracing(dbus::Response
* response
) {
494 LOG(ERROR
) << "Failed to request systrace start";
499 // Called when a CheckValidity response is received.
500 void OnCheckValidityRequestStopSystem(
501 dbus::FileDescriptor
* file_descriptor
,
502 const StopSystemTracingCallback
& callback
) {
503 // Issue the dbus request to stop system tracing
504 dbus::MethodCall
method_call(
505 debugd::kDebugdInterface
,
506 debugd::kSystraceStop
);
507 dbus::MessageWriter
writer(&method_call
);
508 writer
.AppendFileDescriptor(*file_descriptor
);
510 callback_
= callback
;
512 DVLOG(1) << "Requesting a systrace stop";
513 debugdaemon_proxy_
->CallMethod(
515 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
516 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
517 weak_ptr_factory_
.GetWeakPtr()));
519 pipe_reader_
->CloseWriteFD(); // close our copy of fd after send
522 // Called when a response for RequestStopSystemTracing() is received.
523 void OnRequestStopSystemTracing(dbus::Response
* response
) {
525 LOG(ERROR
) << "Failed to request systrace stop";
526 // If debugd crashes or completes I/O before this message is processed
527 // then pipe_reader_ can be NULL, see OnIOComplete().
528 if (pipe_reader_
.get())
529 pipe_reader_
->OnDataReady(-1); // terminate data stream
531 // NB: requester is signaled when i/o completes
534 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
536 if (response
&& dbus::MessageReader(response
).PopString(&status
))
537 callback
.Run(true, status
);
539 callback
.Run(false, "");
542 // Called when pipe i/o completes; pass data on and delete the instance.
543 void OnIOComplete() {
544 callback_
.Run(base::RefCountedString::TakeString(pipe_reader_
->data()));
545 pipe_reader_
.reset();
548 dbus::ObjectProxy
* debugdaemon_proxy_
;
549 scoped_ptr
<PipeReader
> pipe_reader_
;
550 StopSystemTracingCallback callback_
;
551 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
553 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
556 // The DebugDaemonClient implementation used on Linux desktop,
557 // which does nothing.
558 class DebugDaemonClientStubImpl
: public DebugDaemonClient
{
559 // DebugDaemonClient overrides.
560 virtual void GetDebugLogs(base::PlatformFile file
,
561 const GetDebugLogsCallback
& callback
) OVERRIDE
{
564 virtual void SetDebugMode(const std::string
& subsystem
,
565 const SetDebugModeCallback
& callback
) OVERRIDE
{
568 virtual void StartSystemTracing() OVERRIDE
{}
569 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
572 callback
.Run(base::RefCountedString::TakeString(&no_data
));
575 virtual void GetRoutes(bool numeric
, bool ipv6
,
576 const GetRoutesCallback
& callback
) OVERRIDE
{
577 std::vector
<std::string
> empty
;
578 base::MessageLoop::current()->PostTask(FROM_HERE
,
579 base::Bind(callback
, false, empty
));
581 virtual void GetNetworkStatus(const GetNetworkStatusCallback
& callback
)
583 base::MessageLoop::current()->PostTask(FROM_HERE
,
584 base::Bind(callback
, false, ""));
586 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
588 base::MessageLoop::current()->PostTask(FROM_HERE
,
589 base::Bind(callback
, false, ""));
591 virtual void GetNetworkInterfaces(
592 const GetNetworkInterfacesCallback
& callback
) OVERRIDE
{
593 base::MessageLoop::current()->PostTask(FROM_HERE
,
594 base::Bind(callback
, false, ""));
596 virtual void GetPerfData(uint32_t duration
,
597 const GetPerfDataCallback
& callback
) OVERRIDE
{
598 std::vector
<uint8
> data
;
599 base::MessageLoop::current()->PostTask(FROM_HERE
,
600 base::Bind(callback
, data
));
602 virtual void GetAllLogs(const GetLogsCallback
& callback
) OVERRIDE
{
603 std::map
<std::string
, std::string
> empty
;
604 base::MessageLoop::current()->PostTask(FROM_HERE
,
605 base::Bind(callback
, false, empty
));
607 virtual void GetUserLogFiles(const GetLogsCallback
& callback
) OVERRIDE
{
608 std::map
<std::string
, std::string
> user_logs
;
609 user_logs
["preferences"] = "Preferences";
610 user_logs
["invalid_file"] = "Invalid File";
611 base::MessageLoop::current()->PostTask(FROM_HERE
,
612 base::Bind(callback
, true, user_logs
));
615 virtual void TestICMP(const std::string
& ip_address
,
616 const TestICMPCallback
& callback
) OVERRIDE
{
617 base::MessageLoop::current()->PostTask(FROM_HERE
,
618 base::Bind(callback
, false, ""));
622 DebugDaemonClient::DebugDaemonClient() {
625 DebugDaemonClient::~DebugDaemonClient() {
629 DebugDaemonClient::StopSystemTracingCallback
630 DebugDaemonClient::EmptyStopSystemTracingCallback() {
631 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
635 DebugDaemonClient
* DebugDaemonClient::Create(DBusClientImplementationType type
,
637 if (type
== REAL_DBUS_CLIENT_IMPLEMENTATION
)
638 return new DebugDaemonClientImpl(bus
);
639 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION
, type
);
640 return new DebugDaemonClientStubImpl();
643 } // namespace chromeos