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/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 "chromeos/dbus/pipe_reader.h"
24 #include "dbus/message.h"
25 #include "dbus/object_path.h"
26 #include "dbus/object_proxy.h"
27 #include "third_party/cros_system_api/dbus/service_constants.h"
31 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
32 void EmptyStopSystemTracingCallbackBody(
33 const scoped_refptr
<base::RefCountedString
>& unused_result
) {
40 // The DebugDaemonClient implementation used in production.
41 class DebugDaemonClientImpl
: public DebugDaemonClient
{
43 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL
), weak_ptr_factory_(this) {}
45 ~DebugDaemonClientImpl() override
{}
47 // DebugDaemonClient override.
48 void DumpDebugLogs(bool is_compressed
,
50 scoped_refptr
<base::TaskRunner
> task_runner
,
51 const GetDebugLogsCallback
& callback
) override
{
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 task_runner
->PostTaskAndReply(
58 base::Bind(&dbus::FileDescriptor::CheckValidity
,
59 base::Unretained(file_descriptor
)),
60 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
61 weak_ptr_factory_
.GetWeakPtr(),
63 base::Owned(file_descriptor
),
67 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(
75 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
76 base::Bind(&DebugDaemonClientImpl::OnSetDebugMode
,
77 weak_ptr_factory_
.GetWeakPtr(),
81 void GetRoutes(bool numeric
,
83 const GetRoutesCallback
& callback
) override
{
84 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
86 dbus::MessageWriter
writer(&method_call
);
87 dbus::MessageWriter
sub_writer(NULL
);
88 writer
.OpenArray("{sv}", &sub_writer
);
89 dbus::MessageWriter
elem_writer(NULL
);
90 sub_writer
.OpenDictEntry(&elem_writer
);
91 elem_writer
.AppendString("numeric");
92 elem_writer
.AppendVariantOfBool(numeric
);
93 sub_writer
.CloseContainer(&elem_writer
);
94 sub_writer
.OpenDictEntry(&elem_writer
);
95 elem_writer
.AppendString("v6");
96 elem_writer
.AppendVariantOfBool(ipv6
);
97 sub_writer
.CloseContainer(&elem_writer
);
98 writer
.CloseContainer(&sub_writer
);
99 debugdaemon_proxy_
->CallMethod(
101 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
102 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
103 weak_ptr_factory_
.GetWeakPtr(),
107 void GetNetworkStatus(const GetNetworkStatusCallback
& callback
) override
{
108 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
109 debugd::kGetNetworkStatus
);
110 debugdaemon_proxy_
->CallMethod(
112 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
113 base::Bind(&DebugDaemonClientImpl::OnGetNetworkStatus
,
114 weak_ptr_factory_
.GetWeakPtr(),
118 void GetModemStatus(const GetModemStatusCallback
& callback
) override
{
119 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
120 debugd::kGetModemStatus
);
121 debugdaemon_proxy_
->CallMethod(
123 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
124 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
125 weak_ptr_factory_
.GetWeakPtr(),
129 void GetWiMaxStatus(const GetWiMaxStatusCallback
& callback
) override
{
130 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
131 debugd::kGetWiMaxStatus
);
132 debugdaemon_proxy_
->CallMethod(
134 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
135 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus
,
136 weak_ptr_factory_
.GetWeakPtr(),
140 void GetNetworkInterfaces(
141 const GetNetworkInterfacesCallback
& callback
) override
{
142 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
143 debugd::kGetInterfaces
);
144 debugdaemon_proxy_
->CallMethod(
146 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
147 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
148 weak_ptr_factory_
.GetWeakPtr(),
152 void GetPerfData(uint32_t duration
,
153 const GetPerfDataCallback
& callback
) override
{
154 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
155 debugd::kGetRichPerfData
);
156 dbus::MessageWriter
writer(&method_call
);
157 writer
.AppendUint32(duration
);
159 debugdaemon_proxy_
->CallMethod(
161 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
162 base::Bind(&DebugDaemonClientImpl::OnGetPerfData
,
163 weak_ptr_factory_
.GetWeakPtr(),
167 void GetScrubbedLogs(const GetLogsCallback
& callback
) override
{
168 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
169 debugd::kGetFeedbackLogs
);
170 debugdaemon_proxy_
->CallMethod(
172 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
173 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
174 weak_ptr_factory_
.GetWeakPtr(),
178 void GetAllLogs(const GetLogsCallback
& callback
) override
{
179 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
180 debugd::kGetAllLogs
);
181 debugdaemon_proxy_
->CallMethod(
183 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
184 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
185 weak_ptr_factory_
.GetWeakPtr(),
189 void GetUserLogFiles(const GetLogsCallback
& callback
) override
{
190 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
191 debugd::kGetUserLogFiles
);
192 debugdaemon_proxy_
->CallMethod(
194 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
195 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
196 weak_ptr_factory_
.GetWeakPtr(),
200 void StartSystemTracing() override
{
201 dbus::MethodCall
method_call(
202 debugd::kDebugdInterface
,
203 debugd::kSystraceStart
);
204 dbus::MessageWriter
writer(&method_call
);
205 writer
.AppendString("all"); // TODO(sleffler) parameterize category list
207 DVLOG(1) << "Requesting a systrace start";
208 debugdaemon_proxy_
->CallMethod(
210 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
211 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
212 weak_ptr_factory_
.GetWeakPtr()));
215 bool RequestStopSystemTracing(
216 scoped_refptr
<base::TaskRunner
> task_runner
,
217 const StopSystemTracingCallback
& callback
) override
{
218 if (pipe_reader_
!= NULL
) {
219 LOG(ERROR
) << "Busy doing StopSystemTracing";
223 pipe_reader_
.reset(new PipeReaderForString(
225 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
226 weak_ptr_factory_
.GetWeakPtr())));
228 base::File pipe_write_end
= pipe_reader_
->StartIO();
229 // Create dbus::FileDescriptor on the worker thread; on return we'll
230 // issue the D-Bus request to stop tracing and collect results.
231 base::PostTaskAndReplyWithResult(
235 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing
,
236 base::Passed(&pipe_write_end
)),
238 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem
,
239 weak_ptr_factory_
.GetWeakPtr(),
244 void TestICMP(const std::string
& ip_address
,
245 const TestICMPCallback
& callback
) override
{
246 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
248 dbus::MessageWriter
writer(&method_call
);
249 writer
.AppendString(ip_address
);
250 debugdaemon_proxy_
->CallMethod(
252 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
253 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
254 weak_ptr_factory_
.GetWeakPtr(),
258 void TestICMPWithOptions(const std::string
& ip_address
,
259 const std::map
<std::string
, std::string
>& options
,
260 const TestICMPCallback
& callback
) override
{
261 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
262 debugd::kTestICMPWithOptions
);
263 dbus::MessageWriter
writer(&method_call
);
264 dbus::MessageWriter
sub_writer(NULL
);
265 dbus::MessageWriter
elem_writer(NULL
);
268 writer
.AppendString(ip_address
);
270 // Write the options.
271 writer
.OpenArray("{ss}", &sub_writer
);
272 std::map
<std::string
, std::string
>::const_iterator it
;
273 for (it
= options
.begin(); it
!= options
.end(); ++it
) {
274 sub_writer
.OpenDictEntry(&elem_writer
);
275 elem_writer
.AppendString(it
->first
);
276 elem_writer
.AppendString(it
->second
);
277 sub_writer
.CloseContainer(&elem_writer
);
279 writer
.CloseContainer(&sub_writer
);
281 // Call the function.
282 debugdaemon_proxy_
->CallMethod(
284 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
285 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
286 weak_ptr_factory_
.GetWeakPtr(),
290 void UploadCrashes() override
{
291 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
292 debugd::kUploadCrashes
);
293 debugdaemon_proxy_
->CallMethod(
295 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
296 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
297 weak_ptr_factory_
.GetWeakPtr()));
300 void EnableDebuggingFeatures(
301 const std::string
& password
,
302 const EnableDebuggingCallback
& callback
) override
{
303 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
304 debugd::kEnableChromeDevFeatures
);
305 dbus::MessageWriter
writer(&method_call
);
306 writer
.AppendString(password
);
307 debugdaemon_proxy_
->CallMethod(
309 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
310 base::Bind(&DebugDaemonClientImpl::OnEnableDebuggingFeatures
,
311 weak_ptr_factory_
.GetWeakPtr(),
315 void QueryDebuggingFeatures(
316 const QueryDevFeaturesCallback
& callback
) override
{
317 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
318 debugd::kQueryDevFeatures
);
319 dbus::MessageWriter
writer(&method_call
);
320 debugdaemon_proxy_
->CallMethod(
322 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
323 base::Bind(&DebugDaemonClientImpl::OnQueryDebuggingFeatures
,
324 weak_ptr_factory_
.GetWeakPtr(),
328 void RemoveRootfsVerification(
329 const EnableDebuggingCallback
& callback
) override
{
330 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
331 debugd::kRemoveRootfsVerification
);
332 dbus::MessageWriter
writer(&method_call
);
333 debugdaemon_proxy_
->CallMethod(
335 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
336 base::Bind(&DebugDaemonClientImpl::OnRemoveRootfsVerification
,
337 weak_ptr_factory_
.GetWeakPtr(),
341 void WaitForServiceToBeAvailable(
342 const WaitForServiceToBeAvailableCallback
& callback
) override
{
343 debugdaemon_proxy_
->WaitForServiceToBeAvailable(callback
);
347 void Init(dbus::Bus
* bus
) override
{
349 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
350 dbus::ObjectPath(debugd::kDebugdServicePath
));
354 // Called when a CheckValidity response is received.
355 void OnCheckValidityGetDebugLogs(bool is_compressed
,
356 dbus::FileDescriptor
* file_descriptor
,
357 const GetDebugLogsCallback
& callback
) {
358 // Issue the dbus request to get debug logs.
359 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
360 debugd::kDumpDebugLogs
);
361 dbus::MessageWriter
writer(&method_call
);
362 writer
.AppendBool(is_compressed
);
363 writer
.AppendFileDescriptor(*file_descriptor
);
365 debugdaemon_proxy_
->CallMethod(
367 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
368 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
369 weak_ptr_factory_
.GetWeakPtr(),
373 // Called when a response for GetDebugLogs() is received.
374 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
375 dbus::Response
* response
) {
377 LOG(ERROR
) << "Failed to get debug logs";
384 // Called when a response for SetDebugMode() is received.
385 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
386 dbus::Response
* response
) {
388 LOG(ERROR
) << "Failed to change debug mode";
395 void OnGetRoutes(const GetRoutesCallback
& callback
,
396 dbus::Response
* response
) {
397 std::vector
<std::string
> routes
;
399 dbus::MessageReader
reader(response
);
400 if (reader
.PopArrayOfStrings(&routes
)) {
401 callback
.Run(true, routes
);
403 LOG(ERROR
) << "Got non-array response from GetRoutes";
404 callback
.Run(false, routes
);
407 callback
.Run(false, routes
);
411 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
412 dbus::Response
* response
) {
414 if (response
&& dbus::MessageReader(response
).PopString(&status
))
415 callback
.Run(true, status
);
417 callback
.Run(false, "");
420 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
421 dbus::Response
* response
) {
423 if (response
&& dbus::MessageReader(response
).PopString(&status
))
424 callback
.Run(true, status
);
426 callback
.Run(false, "");
429 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
430 dbus::Response
* response
) {
432 if (response
&& dbus::MessageReader(response
).PopString(&status
))
433 callback
.Run(true, status
);
435 callback
.Run(false, "");
438 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
439 dbus::Response
* response
) {
441 if (response
&& dbus::MessageReader(response
).PopString(&status
))
442 callback
.Run(true, status
);
444 callback
.Run(false, "");
447 void OnGetPerfData(const GetPerfDataCallback
& callback
,
448 dbus::Response
* response
) {
449 std::vector
<uint8
> data
;
455 dbus::MessageReader
reader(response
);
456 const uint8
* buffer
= NULL
;
458 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
461 // TODO(asharif): Figure out a way to avoid this copy.
462 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
467 void OnGetAllLogs(const GetLogsCallback
& callback
,
468 dbus::Response
* response
) {
469 std::map
<std::string
, std::string
> logs
;
470 bool broken
= false; // did we see a broken (k,v) pair?
471 dbus::MessageReader
sub_reader(NULL
);
472 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
473 callback
.Run(false, logs
);
476 while (sub_reader
.HasMoreData()) {
477 dbus::MessageReader
sub_sub_reader(NULL
);
478 std::string key
, value
;
479 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
480 || !sub_sub_reader
.PopString(&key
)
481 || !sub_sub_reader
.PopString(&value
)) {
487 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
490 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
491 dbus::Response
* response
) {
492 return OnGetAllLogs(callback
, response
);
495 // Called when a response for a simple start is received.
496 void OnStartMethod(dbus::Response
* response
) {
498 LOG(ERROR
) << "Failed to request start";
503 void OnEnableDebuggingFeatures(
504 const EnableDebuggingCallback
& callback
,
505 dbus::Response
* response
) {
506 if (callback
.is_null())
509 callback
.Run(response
!= NULL
);
512 void OnQueryDebuggingFeatures(
513 const QueryDevFeaturesCallback
& callback
,
514 dbus::Response
* response
) {
515 if (callback
.is_null())
518 int32 feature_mask
= DEV_FEATURE_NONE
;
519 if (!response
|| !dbus::MessageReader(response
).PopInt32(&feature_mask
)) {
520 callback
.Run(false, DEV_FEATURES_DISABLED
);
524 callback
.Run(true, feature_mask
);
527 void OnRemoveRootfsVerification(
528 const EnableDebuggingCallback
& callback
,
529 dbus::Response
* response
) {
530 if (callback
.is_null())
533 callback
.Run(response
!= NULL
);
536 // Creates dbus::FileDescriptor from base::File.
537 static scoped_ptr
<dbus::FileDescriptor
>
538 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end
) {
539 if (!pipe_write_end
.IsValid()) {
540 LOG(ERROR
) << "Cannot create pipe reader";
541 // NB: continue anyway to shutdown tracing; toss trace data
542 pipe_write_end
.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
543 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
544 // TODO(sleffler) if this fails AppendFileDescriptor will abort
546 scoped_ptr
<dbus::FileDescriptor
> file_descriptor(new dbus::FileDescriptor
);
547 file_descriptor
->PutValue(pipe_write_end
.TakePlatformFile());
548 file_descriptor
->CheckValidity();
549 return file_descriptor
.Pass();
552 // Called when a CheckValidity response is received.
553 void OnCreateFileDescriptorRequestStopSystem(
554 const StopSystemTracingCallback
& callback
,
555 scoped_ptr
<dbus::FileDescriptor
> file_descriptor
) {
556 DCHECK(file_descriptor
);
558 // Issue the dbus request to stop system tracing
559 dbus::MethodCall
method_call(
560 debugd::kDebugdInterface
,
561 debugd::kSystraceStop
);
562 dbus::MessageWriter
writer(&method_call
);
563 writer
.AppendFileDescriptor(*file_descriptor
);
565 callback_
= callback
;
567 DVLOG(1) << "Requesting a systrace stop";
568 debugdaemon_proxy_
->CallMethod(
570 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
571 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
572 weak_ptr_factory_
.GetWeakPtr()));
575 // Called when a response for RequestStopSystemTracing() is received.
576 void OnRequestStopSystemTracing(dbus::Response
* response
) {
578 LOG(ERROR
) << "Failed to request systrace stop";
579 // If debugd crashes or completes I/O before this message is processed
580 // then pipe_reader_ can be NULL, see OnIOComplete().
581 if (pipe_reader_
.get())
582 pipe_reader_
->OnDataReady(-1); // terminate data stream
584 // NB: requester is signaled when i/o completes
587 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
589 if (response
&& dbus::MessageReader(response
).PopString(&status
))
590 callback
.Run(true, status
);
592 callback
.Run(false, "");
595 // Called when pipe i/o completes; pass data on and delete the instance.
596 void OnIOComplete() {
597 std::string pipe_data
;
598 pipe_reader_
->GetData(&pipe_data
);
599 callback_
.Run(base::RefCountedString::TakeString(&pipe_data
));
600 pipe_reader_
.reset();
603 dbus::ObjectProxy
* debugdaemon_proxy_
;
604 scoped_ptr
<PipeReaderForString
> pipe_reader_
;
605 StopSystemTracingCallback callback_
;
606 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
608 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
611 DebugDaemonClient::DebugDaemonClient() {
614 DebugDaemonClient::~DebugDaemonClient() {
618 DebugDaemonClient::StopSystemTracingCallback
619 DebugDaemonClient::EmptyStopSystemTracingCallback() {
620 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
624 DebugDaemonClient
* DebugDaemonClient::Create() {
625 return new DebugDaemonClientImpl();
628 } // namespace chromeos