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/files/file_path.h"
15 #include "base/location.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/string_util.h"
19 #include "base/task_runner_util.h"
20 #include "chromeos/dbus/pipe_reader.h"
22 #include "dbus/message.h"
23 #include "dbus/object_path.h"
24 #include "dbus/object_proxy.h"
28 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
29 void EmptyStopSystemTracingCallbackBody(
30 const scoped_refptr
<base::RefCountedString
>& unused_result
) {
37 // The DebugDaemonClient implementation used in production.
38 class DebugDaemonClientImpl
: public DebugDaemonClient
{
40 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL
), weak_ptr_factory_(this) {}
42 ~DebugDaemonClientImpl() override
{}
44 // DebugDaemonClient override.
45 void DumpDebugLogs(bool is_compressed
,
47 scoped_refptr
<base::TaskRunner
> task_runner
,
48 const GetDebugLogsCallback
& callback
) override
{
49 dbus::FileDescriptor
* file_descriptor
= new dbus::FileDescriptor
;
50 file_descriptor
->PutValue(file
.TakePlatformFile());
51 // Punt descriptor validity check to a worker thread; on return we'll
52 // issue the D-Bus request to stop tracing and collect results.
53 task_runner
->PostTaskAndReply(
55 base::Bind(&dbus::FileDescriptor::CheckValidity
,
56 base::Unretained(file_descriptor
)),
57 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
58 weak_ptr_factory_
.GetWeakPtr(),
60 base::Owned(file_descriptor
),
64 void SetDebugMode(const std::string
& subsystem
,
65 const SetDebugModeCallback
& callback
) override
{
66 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
67 debugd::kSetDebugMode
);
68 dbus::MessageWriter
writer(&method_call
);
69 writer
.AppendString(subsystem
);
70 debugdaemon_proxy_
->CallMethod(
72 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
73 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode
,
74 weak_ptr_factory_
.GetWeakPtr(),
78 void GetRoutes(bool numeric
,
80 const GetRoutesCallback
& callback
) override
{
81 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
83 dbus::MessageWriter
writer(&method_call
);
84 dbus::MessageWriter
sub_writer(NULL
);
85 writer
.OpenArray("{sv}", &sub_writer
);
86 dbus::MessageWriter
elem_writer(NULL
);
87 sub_writer
.OpenDictEntry(&elem_writer
);
88 elem_writer
.AppendString("numeric");
89 elem_writer
.AppendVariantOfBool(numeric
);
90 sub_writer
.CloseContainer(&elem_writer
);
91 sub_writer
.OpenDictEntry(&elem_writer
);
92 elem_writer
.AppendString("v6");
93 elem_writer
.AppendVariantOfBool(ipv6
);
94 sub_writer
.CloseContainer(&elem_writer
);
95 writer
.CloseContainer(&sub_writer
);
96 debugdaemon_proxy_
->CallMethod(
98 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
99 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
100 weak_ptr_factory_
.GetWeakPtr(),
104 void GetNetworkStatus(const GetNetworkStatusCallback
& callback
) override
{
105 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
106 debugd::kGetNetworkStatus
);
107 debugdaemon_proxy_
->CallMethod(
109 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
110 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus
,
111 weak_ptr_factory_
.GetWeakPtr(),
115 void GetModemStatus(const GetModemStatusCallback
& callback
) override
{
116 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
117 debugd::kGetModemStatus
);
118 debugdaemon_proxy_
->CallMethod(
120 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
121 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
122 weak_ptr_factory_
.GetWeakPtr(),
126 void GetWiMaxStatus(const GetWiMaxStatusCallback
& callback
) override
{
127 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
128 debugd::kGetWiMaxStatus
);
129 debugdaemon_proxy_
->CallMethod(
131 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
132 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus
,
133 weak_ptr_factory_
.GetWeakPtr(),
137 void GetNetworkInterfaces(
138 const GetNetworkInterfacesCallback
& callback
) override
{
139 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
140 debugd::kGetInterfaces
);
141 debugdaemon_proxy_
->CallMethod(
143 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
144 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
145 weak_ptr_factory_
.GetWeakPtr(),
149 void GetPerfData(uint32_t duration
,
150 const GetPerfDataCallback
& callback
) override
{
151 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
152 debugd::kGetRichPerfData
);
153 dbus::MessageWriter
writer(&method_call
);
154 writer
.AppendUint32(duration
);
156 debugdaemon_proxy_
->CallMethod(
158 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
159 base::Bind(&DebugDaemonClientImpl::OnGetPerfData
,
160 weak_ptr_factory_
.GetWeakPtr(),
164 void GetPerfOutput(uint32_t duration
,
165 const GetPerfOutputCallback
& callback
) override
{
166 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
167 debugd::kGetRandomPerfOutput
);
168 dbus::MessageWriter
writer(&method_call
);
169 writer
.AppendUint32(duration
);
171 debugdaemon_proxy_
->CallMethod(
172 &method_call
, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
173 base::Bind(&DebugDaemonClientImpl::OnGetPerfOutput
,
174 weak_ptr_factory_
.GetWeakPtr(), callback
));
177 void GetScrubbedLogs(const GetLogsCallback
& callback
) override
{
178 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
179 debugd::kGetFeedbackLogs
);
180 debugdaemon_proxy_
->CallMethod(
182 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
183 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
184 weak_ptr_factory_
.GetWeakPtr(),
188 void GetAllLogs(const GetLogsCallback
& callback
) override
{
189 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
190 debugd::kGetAllLogs
);
191 debugdaemon_proxy_
->CallMethod(
193 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
194 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
195 weak_ptr_factory_
.GetWeakPtr(),
199 void GetUserLogFiles(const GetLogsCallback
& callback
) override
{
200 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
201 debugd::kGetUserLogFiles
);
202 debugdaemon_proxy_
->CallMethod(
204 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
205 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
206 weak_ptr_factory_
.GetWeakPtr(),
210 void StartSystemTracing() override
{
211 dbus::MethodCall
method_call(
212 debugd::kDebugdInterface
,
213 debugd::kSystraceStart
);
214 dbus::MessageWriter
writer(&method_call
);
215 writer
.AppendString("all"); // TODO(sleffler) parameterize category list
217 DVLOG(1) << "Requesting a systrace start";
218 debugdaemon_proxy_
->CallMethod(
220 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
221 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
222 weak_ptr_factory_
.GetWeakPtr()));
225 bool RequestStopSystemTracing(
226 scoped_refptr
<base::TaskRunner
> task_runner
,
227 const StopSystemTracingCallback
& callback
) override
{
228 if (pipe_reader_
!= NULL
) {
229 LOG(ERROR
) << "Busy doing StopSystemTracing";
233 pipe_reader_
.reset(new PipeReaderForString(
235 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
236 weak_ptr_factory_
.GetWeakPtr())));
238 base::File pipe_write_end
= pipe_reader_
->StartIO();
239 // Create dbus::FileDescriptor on the worker thread; on return we'll
240 // issue the D-Bus request to stop tracing and collect results.
241 base::PostTaskAndReplyWithResult(
245 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing
,
246 base::Passed(&pipe_write_end
)),
248 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem
,
249 weak_ptr_factory_
.GetWeakPtr(),
254 void TestICMP(const std::string
& ip_address
,
255 const TestICMPCallback
& callback
) override
{
256 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
258 dbus::MessageWriter
writer(&method_call
);
259 writer
.AppendString(ip_address
);
260 debugdaemon_proxy_
->CallMethod(
262 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
263 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
264 weak_ptr_factory_
.GetWeakPtr(),
268 void TestICMPWithOptions(const std::string
& ip_address
,
269 const std::map
<std::string
, std::string
>& options
,
270 const TestICMPCallback
& callback
) override
{
271 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
272 debugd::kTestICMPWithOptions
);
273 dbus::MessageWriter
writer(&method_call
);
274 dbus::MessageWriter
sub_writer(NULL
);
275 dbus::MessageWriter
elem_writer(NULL
);
278 writer
.AppendString(ip_address
);
280 // Write the options.
281 writer
.OpenArray("{ss}", &sub_writer
);
282 std::map
<std::string
, std::string
>::const_iterator it
;
283 for (it
= options
.begin(); it
!= options
.end(); ++it
) {
284 sub_writer
.OpenDictEntry(&elem_writer
);
285 elem_writer
.AppendString(it
->first
);
286 elem_writer
.AppendString(it
->second
);
287 sub_writer
.CloseContainer(&elem_writer
);
289 writer
.CloseContainer(&sub_writer
);
291 // Call the function.
292 debugdaemon_proxy_
->CallMethod(
294 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
295 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
296 weak_ptr_factory_
.GetWeakPtr(),
300 void UploadCrashes() override
{
301 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
302 debugd::kUploadCrashes
);
303 debugdaemon_proxy_
->CallMethod(
305 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
306 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
307 weak_ptr_factory_
.GetWeakPtr()));
310 void EnableDebuggingFeatures(
311 const std::string
& password
,
312 const EnableDebuggingCallback
& callback
) override
{
313 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
314 debugd::kEnableChromeDevFeatures
);
315 dbus::MessageWriter
writer(&method_call
);
316 writer
.AppendString(password
);
317 debugdaemon_proxy_
->CallMethod(
319 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
320 base::Bind(&DebugDaemonClientImpl::OnEnableDebuggingFeatures
,
321 weak_ptr_factory_
.GetWeakPtr(),
325 void QueryDebuggingFeatures(
326 const QueryDevFeaturesCallback
& callback
) override
{
327 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
328 debugd::kQueryDevFeatures
);
329 dbus::MessageWriter
writer(&method_call
);
330 debugdaemon_proxy_
->CallMethod(
332 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
333 base::Bind(&DebugDaemonClientImpl::OnQueryDebuggingFeatures
,
334 weak_ptr_factory_
.GetWeakPtr(),
338 void RemoveRootfsVerification(
339 const EnableDebuggingCallback
& callback
) override
{
340 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
341 debugd::kRemoveRootfsVerification
);
342 dbus::MessageWriter
writer(&method_call
);
343 debugdaemon_proxy_
->CallMethod(
345 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
346 base::Bind(&DebugDaemonClientImpl::OnRemoveRootfsVerification
,
347 weak_ptr_factory_
.GetWeakPtr(),
351 void WaitForServiceToBeAvailable(
352 const WaitForServiceToBeAvailableCallback
& callback
) override
{
353 debugdaemon_proxy_
->WaitForServiceToBeAvailable(callback
);
357 void Init(dbus::Bus
* bus
) override
{
359 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
360 dbus::ObjectPath(debugd::kDebugdServicePath
));
364 // Called when a CheckValidity response is received.
365 void OnCheckValidityGetDebugLogs(bool is_compressed
,
366 dbus::FileDescriptor
* file_descriptor
,
367 const GetDebugLogsCallback
& callback
) {
368 // Issue the dbus request to get debug logs.
369 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
370 debugd::kDumpDebugLogs
);
371 dbus::MessageWriter
writer(&method_call
);
372 writer
.AppendBool(is_compressed
);
373 writer
.AppendFileDescriptor(*file_descriptor
);
375 debugdaemon_proxy_
->CallMethod(
377 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
378 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
379 weak_ptr_factory_
.GetWeakPtr(),
383 // Called when a response for GetDebugLogs() is received.
384 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
385 dbus::Response
* response
) {
387 LOG(ERROR
) << "Failed to get debug logs";
394 // Called when a response for SetDebugMode() is received.
395 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
396 dbus::Response
* response
) {
398 LOG(ERROR
) << "Failed to change debug mode";
405 void OnGetRoutes(const GetRoutesCallback
& callback
,
406 dbus::Response
* response
) {
407 std::vector
<std::string
> routes
;
409 dbus::MessageReader
reader(response
);
410 if (reader
.PopArrayOfStrings(&routes
)) {
411 callback
.Run(true, routes
);
413 LOG(ERROR
) << "Got non-array response from GetRoutes";
414 callback
.Run(false, routes
);
417 callback
.Run(false, routes
);
421 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
422 dbus::Response
* response
) {
424 if (response
&& dbus::MessageReader(response
).PopString(&status
))
425 callback
.Run(true, status
);
427 callback
.Run(false, "");
430 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
431 dbus::Response
* response
) {
433 if (response
&& dbus::MessageReader(response
).PopString(&status
))
434 callback
.Run(true, status
);
436 callback
.Run(false, "");
439 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
440 dbus::Response
* response
) {
442 if (response
&& dbus::MessageReader(response
).PopString(&status
))
443 callback
.Run(true, status
);
445 callback
.Run(false, "");
448 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
449 dbus::Response
* response
) {
451 if (response
&& dbus::MessageReader(response
).PopString(&status
))
452 callback
.Run(true, status
);
454 callback
.Run(false, "");
457 void OnGetPerfData(const GetPerfDataCallback
& callback
,
458 dbus::Response
* response
) {
459 std::vector
<uint8
> data
;
465 dbus::MessageReader
reader(response
);
466 const uint8
* buffer
= NULL
;
468 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
471 // TODO(asharif): Figure out a way to avoid this copy.
472 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
477 void OnGetPerfOutput(const GetPerfOutputCallback
& callback
,
478 dbus::Response
* response
) {
482 dbus::MessageReader
reader(response
);
485 if (!reader
.PopInt32(&status
))
488 const uint8
* buffer
= nullptr;
491 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
493 std::vector
<uint8
> perf_data
;
495 perf_data
.insert(perf_data
.end(), buffer
, buffer
+ buf_size
);
497 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
499 std::vector
<uint8
> perf_stat
;
501 perf_stat
.insert(perf_stat
.end(), buffer
, buffer
+ buf_size
);
503 callback
.Run(status
, perf_data
, perf_stat
);
506 void OnGetAllLogs(const GetLogsCallback
& callback
,
507 dbus::Response
* response
) {
508 std::map
<std::string
, std::string
> logs
;
509 bool broken
= false; // did we see a broken (k,v) pair?
510 dbus::MessageReader
sub_reader(NULL
);
511 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
512 callback
.Run(false, logs
);
515 while (sub_reader
.HasMoreData()) {
516 dbus::MessageReader
sub_sub_reader(NULL
);
517 std::string key
, value
;
518 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
519 || !sub_sub_reader
.PopString(&key
)
520 || !sub_sub_reader
.PopString(&value
)) {
526 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
529 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
530 dbus::Response
* response
) {
531 return OnGetAllLogs(callback
, response
);
534 // Called when a response for a simple start is received.
535 void OnStartMethod(dbus::Response
* response
) {
537 LOG(ERROR
) << "Failed to request start";
542 void OnEnableDebuggingFeatures(
543 const EnableDebuggingCallback
& callback
,
544 dbus::Response
* response
) {
545 if (callback
.is_null())
548 callback
.Run(response
!= NULL
);
551 void OnQueryDebuggingFeatures(
552 const QueryDevFeaturesCallback
& callback
,
553 dbus::Response
* response
) {
554 if (callback
.is_null())
557 int32 feature_mask
= DEV_FEATURE_NONE
;
558 if (!response
|| !dbus::MessageReader(response
).PopInt32(&feature_mask
)) {
559 callback
.Run(false, debugd::DevFeatureFlag::DEV_FEATURES_DISABLED
);
563 callback
.Run(true, feature_mask
);
566 void OnRemoveRootfsVerification(
567 const EnableDebuggingCallback
& callback
,
568 dbus::Response
* response
) {
569 if (callback
.is_null())
572 callback
.Run(response
!= NULL
);
575 // Creates dbus::FileDescriptor from base::File.
576 static scoped_ptr
<dbus::FileDescriptor
>
577 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end
) {
578 if (!pipe_write_end
.IsValid()) {
579 LOG(ERROR
) << "Cannot create pipe reader";
580 // NB: continue anyway to shutdown tracing; toss trace data
581 pipe_write_end
.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
582 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
583 // TODO(sleffler) if this fails AppendFileDescriptor will abort
585 scoped_ptr
<dbus::FileDescriptor
> file_descriptor(new dbus::FileDescriptor
);
586 file_descriptor
->PutValue(pipe_write_end
.TakePlatformFile());
587 file_descriptor
->CheckValidity();
588 return file_descriptor
.Pass();
591 // Called when a CheckValidity response is received.
592 void OnCreateFileDescriptorRequestStopSystem(
593 const StopSystemTracingCallback
& callback
,
594 scoped_ptr
<dbus::FileDescriptor
> file_descriptor
) {
595 DCHECK(file_descriptor
);
597 // Issue the dbus request to stop system tracing
598 dbus::MethodCall
method_call(
599 debugd::kDebugdInterface
,
600 debugd::kSystraceStop
);
601 dbus::MessageWriter
writer(&method_call
);
602 writer
.AppendFileDescriptor(*file_descriptor
);
604 callback_
= callback
;
606 DVLOG(1) << "Requesting a systrace stop";
607 debugdaemon_proxy_
->CallMethod(
609 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
610 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
611 weak_ptr_factory_
.GetWeakPtr()));
614 // Called when a response for RequestStopSystemTracing() is received.
615 void OnRequestStopSystemTracing(dbus::Response
* response
) {
617 LOG(ERROR
) << "Failed to request systrace stop";
618 // If debugd crashes or completes I/O before this message is processed
619 // then pipe_reader_ can be NULL, see OnIOComplete().
620 if (pipe_reader_
.get())
621 pipe_reader_
->OnDataReady(-1); // terminate data stream
623 // NB: requester is signaled when i/o completes
626 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
628 if (response
&& dbus::MessageReader(response
).PopString(&status
))
629 callback
.Run(true, status
);
631 callback
.Run(false, "");
634 // Called when pipe i/o completes; pass data on and delete the instance.
635 void OnIOComplete() {
636 std::string pipe_data
;
637 pipe_reader_
->GetData(&pipe_data
);
638 callback_
.Run(base::RefCountedString::TakeString(&pipe_data
));
639 pipe_reader_
.reset();
642 dbus::ObjectProxy
* debugdaemon_proxy_
;
643 scoped_ptr
<PipeReaderForString
> pipe_reader_
;
644 StopSystemTracingCallback callback_
;
645 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
647 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
650 DebugDaemonClient::DebugDaemonClient() {
653 DebugDaemonClient::~DebugDaemonClient() {
657 DebugDaemonClient::StopSystemTracingCallback
658 DebugDaemonClient::EmptyStopSystemTracingCallback() {
659 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
663 DebugDaemonClient
* DebugDaemonClient::Create() {
664 return new DebugDaemonClientImpl();
667 } // namespace chromeos