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 GetScrubbedLogs(const GetLogsCallback
& callback
) override
{
165 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
166 debugd::kGetFeedbackLogs
);
167 debugdaemon_proxy_
->CallMethod(
169 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
170 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
171 weak_ptr_factory_
.GetWeakPtr(),
175 void GetAllLogs(const GetLogsCallback
& callback
) override
{
176 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
177 debugd::kGetAllLogs
);
178 debugdaemon_proxy_
->CallMethod(
180 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
181 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
182 weak_ptr_factory_
.GetWeakPtr(),
186 void GetUserLogFiles(const GetLogsCallback
& callback
) override
{
187 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
188 debugd::kGetUserLogFiles
);
189 debugdaemon_proxy_
->CallMethod(
191 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
192 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
193 weak_ptr_factory_
.GetWeakPtr(),
197 void StartSystemTracing() override
{
198 dbus::MethodCall
method_call(
199 debugd::kDebugdInterface
,
200 debugd::kSystraceStart
);
201 dbus::MessageWriter
writer(&method_call
);
202 writer
.AppendString("all"); // TODO(sleffler) parameterize category list
204 DVLOG(1) << "Requesting a systrace start";
205 debugdaemon_proxy_
->CallMethod(
207 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
208 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
209 weak_ptr_factory_
.GetWeakPtr()));
212 bool RequestStopSystemTracing(
213 scoped_refptr
<base::TaskRunner
> task_runner
,
214 const StopSystemTracingCallback
& callback
) override
{
215 if (pipe_reader_
!= NULL
) {
216 LOG(ERROR
) << "Busy doing StopSystemTracing";
220 pipe_reader_
.reset(new PipeReaderForString(
222 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
223 weak_ptr_factory_
.GetWeakPtr())));
225 base::File pipe_write_end
= pipe_reader_
->StartIO();
226 // Create dbus::FileDescriptor on the worker thread; on return we'll
227 // issue the D-Bus request to stop tracing and collect results.
228 base::PostTaskAndReplyWithResult(
232 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing
,
233 base::Passed(&pipe_write_end
)),
235 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem
,
236 weak_ptr_factory_
.GetWeakPtr(),
241 void TestICMP(const std::string
& ip_address
,
242 const TestICMPCallback
& callback
) override
{
243 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
245 dbus::MessageWriter
writer(&method_call
);
246 writer
.AppendString(ip_address
);
247 debugdaemon_proxy_
->CallMethod(
249 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
250 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
251 weak_ptr_factory_
.GetWeakPtr(),
255 void TestICMPWithOptions(const std::string
& ip_address
,
256 const std::map
<std::string
, std::string
>& options
,
257 const TestICMPCallback
& callback
) override
{
258 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
259 debugd::kTestICMPWithOptions
);
260 dbus::MessageWriter
writer(&method_call
);
261 dbus::MessageWriter
sub_writer(NULL
);
262 dbus::MessageWriter
elem_writer(NULL
);
265 writer
.AppendString(ip_address
);
267 // Write the options.
268 writer
.OpenArray("{ss}", &sub_writer
);
269 std::map
<std::string
, std::string
>::const_iterator it
;
270 for (it
= options
.begin(); it
!= options
.end(); ++it
) {
271 sub_writer
.OpenDictEntry(&elem_writer
);
272 elem_writer
.AppendString(it
->first
);
273 elem_writer
.AppendString(it
->second
);
274 sub_writer
.CloseContainer(&elem_writer
);
276 writer
.CloseContainer(&sub_writer
);
278 // Call the function.
279 debugdaemon_proxy_
->CallMethod(
281 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
282 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
283 weak_ptr_factory_
.GetWeakPtr(),
287 void UploadCrashes() override
{
288 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
289 debugd::kUploadCrashes
);
290 debugdaemon_proxy_
->CallMethod(
292 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
293 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
294 weak_ptr_factory_
.GetWeakPtr()));
297 void EnableDebuggingFeatures(
298 const std::string
& password
,
299 const EnableDebuggingCallback
& callback
) override
{
300 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
301 debugd::kEnableChromeDevFeatures
);
302 dbus::MessageWriter
writer(&method_call
);
303 writer
.AppendString(password
);
304 debugdaemon_proxy_
->CallMethod(
306 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
307 base::Bind(&DebugDaemonClientImpl::OnEnableDebuggingFeatures
,
308 weak_ptr_factory_
.GetWeakPtr(),
312 void QueryDebuggingFeatures(
313 const QueryDevFeaturesCallback
& callback
) override
{
314 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
315 debugd::kQueryDevFeatures
);
316 dbus::MessageWriter
writer(&method_call
);
317 debugdaemon_proxy_
->CallMethod(
319 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
320 base::Bind(&DebugDaemonClientImpl::OnQueryDebuggingFeatures
,
321 weak_ptr_factory_
.GetWeakPtr(),
325 void RemoveRootfsVerification(
326 const EnableDebuggingCallback
& callback
) override
{
327 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
328 debugd::kRemoveRootfsVerification
);
329 dbus::MessageWriter
writer(&method_call
);
330 debugdaemon_proxy_
->CallMethod(
332 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
333 base::Bind(&DebugDaemonClientImpl::OnRemoveRootfsVerification
,
334 weak_ptr_factory_
.GetWeakPtr(),
338 void WaitForServiceToBeAvailable(
339 const WaitForServiceToBeAvailableCallback
& callback
) override
{
340 debugdaemon_proxy_
->WaitForServiceToBeAvailable(callback
);
344 void Init(dbus::Bus
* bus
) override
{
346 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
347 dbus::ObjectPath(debugd::kDebugdServicePath
));
351 // Called when a CheckValidity response is received.
352 void OnCheckValidityGetDebugLogs(bool is_compressed
,
353 dbus::FileDescriptor
* file_descriptor
,
354 const GetDebugLogsCallback
& callback
) {
355 // Issue the dbus request to get debug logs.
356 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
357 debugd::kDumpDebugLogs
);
358 dbus::MessageWriter
writer(&method_call
);
359 writer
.AppendBool(is_compressed
);
360 writer
.AppendFileDescriptor(*file_descriptor
);
362 debugdaemon_proxy_
->CallMethod(
364 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
365 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
366 weak_ptr_factory_
.GetWeakPtr(),
370 // Called when a response for GetDebugLogs() is received.
371 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
372 dbus::Response
* response
) {
374 LOG(ERROR
) << "Failed to get debug logs";
381 // Called when a response for SetDebugMode() is received.
382 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
383 dbus::Response
* response
) {
385 LOG(ERROR
) << "Failed to change debug mode";
392 void OnGetRoutes(const GetRoutesCallback
& callback
,
393 dbus::Response
* response
) {
394 std::vector
<std::string
> routes
;
396 dbus::MessageReader
reader(response
);
397 if (reader
.PopArrayOfStrings(&routes
)) {
398 callback
.Run(true, routes
);
400 LOG(ERROR
) << "Got non-array response from GetRoutes";
401 callback
.Run(false, routes
);
404 callback
.Run(false, routes
);
408 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
409 dbus::Response
* response
) {
411 if (response
&& dbus::MessageReader(response
).PopString(&status
))
412 callback
.Run(true, status
);
414 callback
.Run(false, "");
417 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
418 dbus::Response
* response
) {
420 if (response
&& dbus::MessageReader(response
).PopString(&status
))
421 callback
.Run(true, status
);
423 callback
.Run(false, "");
426 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
427 dbus::Response
* response
) {
429 if (response
&& dbus::MessageReader(response
).PopString(&status
))
430 callback
.Run(true, status
);
432 callback
.Run(false, "");
435 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
436 dbus::Response
* response
) {
438 if (response
&& dbus::MessageReader(response
).PopString(&status
))
439 callback
.Run(true, status
);
441 callback
.Run(false, "");
444 void OnGetPerfData(const GetPerfDataCallback
& callback
,
445 dbus::Response
* response
) {
446 std::vector
<uint8
> data
;
452 dbus::MessageReader
reader(response
);
453 const uint8
* buffer
= NULL
;
455 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
458 // TODO(asharif): Figure out a way to avoid this copy.
459 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
464 void OnGetAllLogs(const GetLogsCallback
& callback
,
465 dbus::Response
* response
) {
466 std::map
<std::string
, std::string
> logs
;
467 bool broken
= false; // did we see a broken (k,v) pair?
468 dbus::MessageReader
sub_reader(NULL
);
469 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
470 callback
.Run(false, logs
);
473 while (sub_reader
.HasMoreData()) {
474 dbus::MessageReader
sub_sub_reader(NULL
);
475 std::string key
, value
;
476 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
477 || !sub_sub_reader
.PopString(&key
)
478 || !sub_sub_reader
.PopString(&value
)) {
484 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
487 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
488 dbus::Response
* response
) {
489 return OnGetAllLogs(callback
, response
);
492 // Called when a response for a simple start is received.
493 void OnStartMethod(dbus::Response
* response
) {
495 LOG(ERROR
) << "Failed to request start";
500 void OnEnableDebuggingFeatures(
501 const EnableDebuggingCallback
& callback
,
502 dbus::Response
* response
) {
503 if (callback
.is_null())
506 callback
.Run(response
!= NULL
);
509 void OnQueryDebuggingFeatures(
510 const QueryDevFeaturesCallback
& callback
,
511 dbus::Response
* response
) {
512 if (callback
.is_null())
515 int32 feature_mask
= DEV_FEATURE_NONE
;
516 if (!response
|| !dbus::MessageReader(response
).PopInt32(&feature_mask
)) {
517 callback
.Run(false, debugd::DevFeatureFlag::DEV_FEATURES_DISABLED
);
521 callback
.Run(true, feature_mask
);
524 void OnRemoveRootfsVerification(
525 const EnableDebuggingCallback
& callback
,
526 dbus::Response
* response
) {
527 if (callback
.is_null())
530 callback
.Run(response
!= NULL
);
533 // Creates dbus::FileDescriptor from base::File.
534 static scoped_ptr
<dbus::FileDescriptor
>
535 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end
) {
536 if (!pipe_write_end
.IsValid()) {
537 LOG(ERROR
) << "Cannot create pipe reader";
538 // NB: continue anyway to shutdown tracing; toss trace data
539 pipe_write_end
.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
540 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
541 // TODO(sleffler) if this fails AppendFileDescriptor will abort
543 scoped_ptr
<dbus::FileDescriptor
> file_descriptor(new dbus::FileDescriptor
);
544 file_descriptor
->PutValue(pipe_write_end
.TakePlatformFile());
545 file_descriptor
->CheckValidity();
546 return file_descriptor
.Pass();
549 // Called when a CheckValidity response is received.
550 void OnCreateFileDescriptorRequestStopSystem(
551 const StopSystemTracingCallback
& callback
,
552 scoped_ptr
<dbus::FileDescriptor
> file_descriptor
) {
553 DCHECK(file_descriptor
);
555 // Issue the dbus request to stop system tracing
556 dbus::MethodCall
method_call(
557 debugd::kDebugdInterface
,
558 debugd::kSystraceStop
);
559 dbus::MessageWriter
writer(&method_call
);
560 writer
.AppendFileDescriptor(*file_descriptor
);
562 callback_
= callback
;
564 DVLOG(1) << "Requesting a systrace stop";
565 debugdaemon_proxy_
->CallMethod(
567 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
568 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
569 weak_ptr_factory_
.GetWeakPtr()));
572 // Called when a response for RequestStopSystemTracing() is received.
573 void OnRequestStopSystemTracing(dbus::Response
* response
) {
575 LOG(ERROR
) << "Failed to request systrace stop";
576 // If debugd crashes or completes I/O before this message is processed
577 // then pipe_reader_ can be NULL, see OnIOComplete().
578 if (pipe_reader_
.get())
579 pipe_reader_
->OnDataReady(-1); // terminate data stream
581 // NB: requester is signaled when i/o completes
584 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
586 if (response
&& dbus::MessageReader(response
).PopString(&status
))
587 callback
.Run(true, status
);
589 callback
.Run(false, "");
592 // Called when pipe i/o completes; pass data on and delete the instance.
593 void OnIOComplete() {
594 std::string pipe_data
;
595 pipe_reader_
->GetData(&pipe_data
);
596 callback_
.Run(base::RefCountedString::TakeString(&pipe_data
));
597 pipe_reader_
.reset();
600 dbus::ObjectProxy
* debugdaemon_proxy_
;
601 scoped_ptr
<PipeReaderForString
> pipe_reader_
;
602 StopSystemTracingCallback callback_
;
603 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
605 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
608 DebugDaemonClient::DebugDaemonClient() {
611 DebugDaemonClient::~DebugDaemonClient() {
615 DebugDaemonClient::StopSystemTracingCallback
616 DebugDaemonClient::EmptyStopSystemTracingCallback() {
617 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
621 DebugDaemonClient
* DebugDaemonClient::Create() {
622 return new DebugDaemonClientImpl();
625 } // namespace chromeos