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.
5 #include "chromeos/dbus/debug_daemon_client.h"
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"
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"
33 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
34 void EmptyStopSystemTracingCallbackBody(
35 const scoped_refptr
<base::RefCountedString
>& unused_result
) {
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.
43 // TODO(sleffler) move data collection to a sub-class so this
44 // can be reused to process data as it is received
47 typedef base::Callback
<void(void)>IOCompleteCallback
;
49 explicit PipeReader(IOCompleteCallback callback
)
50 : io_buffer_(new net::IOBufferWithSize(4096)),
52 weak_ptr_factory_(this) {
53 pipe_fd_
[0] = pipe_fd_
[1] = -1;
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]";
63 // Returns descriptor for the writeable side of the pipe.
64 int GetWriteFD() { return pipe_fd_
[1]; }
66 // Closes writeable descriptor; normally used in parent process after fork.
68 if (pipe_fd_
[1] != -1) {
69 if (IGNORE_EINTR(close(pipe_fd_
[1])) < 0)
70 PLOG(ERROR
) << "close";
75 // Returns collected data.
76 std::string
* data() { return &data_
; }
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).
83 // Use a pipe to collect data
84 const int status
= HANDLE_EINTR(pipe(pipe_fd_
));
86 PLOG(ERROR
) << "pipe";
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
,
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";
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
112 data_
.append(io_buffer_
->data(), byte_count
);
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?
124 friend class base::RefCounted
<PipeReader
>;
127 scoped_ptr
<net::FileStream
> data_stream_
;
128 scoped_refptr
<net::IOBufferWithSize
> io_buffer_
;
130 IOCompleteCallback callback_
;
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_
;
136 DISALLOW_COPY_AND_ASSIGN(PipeReader
);
143 // The DebugDaemonClient implementation used in production.
144 class DebugDaemonClientImpl
: public DebugDaemonClient
{
146 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL
), weak_ptr_factory_(this) {}
148 virtual ~DebugDaemonClientImpl() {}
150 // DebugDaemonClient override.
151 virtual void GetDebugLogs(base::PlatformFile file
,
152 const GetDebugLogsCallback
& callback
) OVERRIDE
{
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(
159 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
161 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
162 weak_ptr_factory_
.GetWeakPtr(),
163 base::Owned(file_descriptor
),
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(
176 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
177 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode
,
178 weak_ptr_factory_
.GetWeakPtr(),
182 virtual void GetRoutes(bool numeric
, bool ipv6
,
183 const GetRoutesCallback
& callback
) OVERRIDE
{
184 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
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(
201 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
202 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
203 weak_ptr_factory_
.GetWeakPtr(),
207 virtual void GetNetworkStatus(const GetNetworkStatusCallback
& callback
)
209 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
210 debugd::kGetNetworkStatus
);
211 debugdaemon_proxy_
->CallMethod(
213 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
214 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus
,
215 weak_ptr_factory_
.GetWeakPtr(),
219 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
221 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
222 debugd::kGetModemStatus
);
223 debugdaemon_proxy_
->CallMethod(
225 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
226 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
227 weak_ptr_factory_
.GetWeakPtr(),
231 virtual void GetWiMaxStatus(const GetWiMaxStatusCallback
& callback
)
233 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
234 debugd::kGetWiMaxStatus
);
235 debugdaemon_proxy_
->CallMethod(
237 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
238 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus
,
239 weak_ptr_factory_
.GetWeakPtr(),
243 virtual void GetNetworkInterfaces(
244 const GetNetworkInterfacesCallback
& callback
) OVERRIDE
{
245 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
246 debugd::kGetInterfaces
);
247 debugdaemon_proxy_
->CallMethod(
249 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
250 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
251 weak_ptr_factory_
.GetWeakPtr(),
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
);
262 debugdaemon_proxy_
->CallMethod(
264 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
265 base::Bind(&DebugDaemonClientImpl::OnGetPerfData
,
266 weak_ptr_factory_
.GetWeakPtr(),
270 virtual void GetScrubbedLogs(const GetLogsCallback
& callback
) OVERRIDE
{
271 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
272 debugd::kGetFeedbackLogs
);
273 debugdaemon_proxy_
->CallMethod(
275 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
276 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
277 weak_ptr_factory_
.GetWeakPtr(),
281 virtual void GetAllLogs(const GetLogsCallback
& callback
)
283 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
284 debugd::kGetAllLogs
);
285 debugdaemon_proxy_
->CallMethod(
287 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
288 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
289 weak_ptr_factory_
.GetWeakPtr(),
293 virtual void GetUserLogFiles(
294 const GetLogsCallback
& callback
) OVERRIDE
{
295 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
296 debugd::kGetUserLogFiles
);
297 debugdaemon_proxy_
->CallMethod(
299 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
300 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
301 weak_ptr_factory_
.GetWeakPtr(),
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
312 DVLOG(1) << "Requesting a systrace start";
313 debugdaemon_proxy_
->CallMethod(
315 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
316 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing
,
317 weak_ptr_factory_
.GetWeakPtr()));
320 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
322 if (pipe_reader_
!= NULL
) {
323 LOG(ERROR
) << "Busy doing StopSystemTracing";
327 pipe_reader_
.reset(new PipeReader(
328 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
329 weak_ptr_factory_
.GetWeakPtr())));
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
337 write_fd
= pipe_reader_
->GetWriteFD();
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(
345 base::Bind(&DebugDaemonClientImpl::CheckValidity
,
347 base::Bind(&DebugDaemonClientImpl::OnCheckValidityRequestStopSystem
,
348 weak_ptr_factory_
.GetWeakPtr(),
349 base::Owned(file_descriptor
),
356 virtual void TestICMP(const std::string
& ip_address
,
357 const TestICMPCallback
& callback
) OVERRIDE
{
358 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
360 dbus::MessageWriter
writer(&method_call
);
361 writer
.AppendString(ip_address
);
362 debugdaemon_proxy_
->CallMethod(
364 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
365 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
366 weak_ptr_factory_
.GetWeakPtr(),
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
);
381 writer
.AppendString(ip_address
);
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
);
392 writer
.CloseContainer(&sub_writer
);
394 // Call the function.
395 debugdaemon_proxy_
->CallMethod(
397 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
398 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
399 weak_ptr_factory_
.GetWeakPtr(),
404 virtual void Init(dbus::Bus
* bus
) OVERRIDE
{
406 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
407 dbus::ObjectPath(debugd::kDebugdServicePath
));
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();
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
);
426 debugdaemon_proxy_
->CallMethod(
428 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
429 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
430 weak_ptr_factory_
.GetWeakPtr(),
434 // Called when a response for GetDebugLogs() is received.
435 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
436 dbus::Response
* response
) {
438 LOG(ERROR
) << "Failed to get debug logs";
445 // Called when a response for SetDebugMode() is received.
446 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
447 dbus::Response
* response
) {
449 LOG(ERROR
) << "Failed to change debug mode";
456 void OnGetRoutes(const GetRoutesCallback
& callback
,
457 dbus::Response
* response
) {
458 std::vector
<std::string
> routes
;
460 dbus::MessageReader
reader(response
);
461 if (reader
.PopArrayOfStrings(&routes
)) {
462 callback
.Run(true, routes
);
464 LOG(ERROR
) << "Got non-array response from GetRoutes";
465 callback
.Run(false, routes
);
468 callback
.Run(false, routes
);
472 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
473 dbus::Response
* response
) {
475 if (response
&& dbus::MessageReader(response
).PopString(&status
))
476 callback
.Run(true, status
);
478 callback
.Run(false, "");
481 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
482 dbus::Response
* response
) {
484 if (response
&& dbus::MessageReader(response
).PopString(&status
))
485 callback
.Run(true, status
);
487 callback
.Run(false, "");
490 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
491 dbus::Response
* response
) {
493 if (response
&& dbus::MessageReader(response
).PopString(&status
))
494 callback
.Run(true, status
);
496 callback
.Run(false, "");
499 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
500 dbus::Response
* response
) {
502 if (response
&& dbus::MessageReader(response
).PopString(&status
))
503 callback
.Run(true, status
);
505 callback
.Run(false, "");
508 void OnGetPerfData(const GetPerfDataCallback
& callback
,
509 dbus::Response
* response
) {
510 std::vector
<uint8
> data
;
516 dbus::MessageReader
reader(response
);
517 const uint8
* buffer
= NULL
;
519 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
522 // TODO(asharif): Figure out a way to avoid this copy.
523 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
528 void OnGetAllLogs(const GetLogsCallback
& callback
,
529 dbus::Response
* response
) {
530 std::map
<std::string
, std::string
> logs
;
531 bool broken
= false; // did we see a broken (k,v) pair?
532 dbus::MessageReader
sub_reader(NULL
);
533 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
534 callback
.Run(false, logs
);
537 while (sub_reader
.HasMoreData()) {
538 dbus::MessageReader
sub_sub_reader(NULL
);
539 std::string key
, value
;
540 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
541 || !sub_sub_reader
.PopString(&key
)
542 || !sub_sub_reader
.PopString(&value
)) {
548 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
551 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
552 dbus::Response
* response
) {
553 return OnGetAllLogs(callback
, response
);
556 // Called when a response for StartSystemTracing() is received.
557 void OnStartSystemTracing(dbus::Response
* response
) {
559 LOG(ERROR
) << "Failed to request systrace start";
564 // Called when a CheckValidity response is received.
565 void OnCheckValidityRequestStopSystem(
566 dbus::FileDescriptor
* file_descriptor
,
567 const StopSystemTracingCallback
& callback
) {
568 // Issue the dbus request to stop system tracing
569 dbus::MethodCall
method_call(
570 debugd::kDebugdInterface
,
571 debugd::kSystraceStop
);
572 dbus::MessageWriter
writer(&method_call
);
573 writer
.AppendFileDescriptor(*file_descriptor
);
575 callback_
= callback
;
577 DVLOG(1) << "Requesting a systrace stop";
578 debugdaemon_proxy_
->CallMethod(
580 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
581 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
582 weak_ptr_factory_
.GetWeakPtr()));
584 pipe_reader_
->CloseWriteFD(); // close our copy of fd after send
587 // Called when a response for RequestStopSystemTracing() is received.
588 void OnRequestStopSystemTracing(dbus::Response
* response
) {
590 LOG(ERROR
) << "Failed to request systrace stop";
591 // If debugd crashes or completes I/O before this message is processed
592 // then pipe_reader_ can be NULL, see OnIOComplete().
593 if (pipe_reader_
.get())
594 pipe_reader_
->OnDataReady(-1); // terminate data stream
596 // NB: requester is signaled when i/o completes
599 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
601 if (response
&& dbus::MessageReader(response
).PopString(&status
))
602 callback
.Run(true, status
);
604 callback
.Run(false, "");
607 // Called when pipe i/o completes; pass data on and delete the instance.
608 void OnIOComplete() {
609 callback_
.Run(base::RefCountedString::TakeString(pipe_reader_
->data()));
610 pipe_reader_
.reset();
613 dbus::ObjectProxy
* debugdaemon_proxy_
;
614 scoped_ptr
<PipeReader
> pipe_reader_
;
615 StopSystemTracingCallback callback_
;
616 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
618 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
621 DebugDaemonClient::DebugDaemonClient() {
624 DebugDaemonClient::~DebugDaemonClient() {
628 DebugDaemonClient::StopSystemTracingCallback
629 DebugDaemonClient::EmptyStopSystemTracingCallback() {
630 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
634 DebugDaemonClient
* DebugDaemonClient::Create() {
635 return new DebugDaemonClientImpl();
638 } // namespace chromeos