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 "base/threading/worker_pool.h"
23 #include "chromeos/dbus/pipe_reader.h"
25 #include "dbus/message.h"
26 #include "dbus/object_path.h"
27 #include "dbus/object_proxy.h"
28 #include "third_party/cros_system_api/dbus/service_constants.h"
32 // Used in DebugDaemonClient::EmptySystemStopTracingCallback().
33 void EmptyStopSystemTracingCallbackBody(
34 const scoped_refptr
<base::RefCountedString
>& unused_result
) {
41 // The DebugDaemonClient implementation used in production.
42 class DebugDaemonClientImpl
: public DebugDaemonClient
{
44 DebugDaemonClientImpl() : debugdaemon_proxy_(NULL
), weak_ptr_factory_(this) {}
46 virtual ~DebugDaemonClientImpl() {}
48 // DebugDaemonClient override.
49 virtual void GetDebugLogs(base::File file
,
50 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 base::WorkerPool::PostTaskAndReply(
58 base::Bind(&dbus::FileDescriptor::CheckValidity
,
59 base::Unretained(file_descriptor
)),
60 base::Bind(&DebugDaemonClientImpl::OnCheckValidityGetDebugLogs
,
61 weak_ptr_factory_
.GetWeakPtr(),
62 base::Owned(file_descriptor
),
67 virtual 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 virtual void GetRoutes(bool numeric
, bool ipv6
,
82 const GetRoutesCallback
& callback
) OVERRIDE
{
83 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
85 dbus::MessageWriter
writer(&method_call
);
86 dbus::MessageWriter
sub_writer(NULL
);
87 writer
.OpenArray("{sv}", &sub_writer
);
88 dbus::MessageWriter
elem_writer(NULL
);
89 sub_writer
.OpenDictEntry(&elem_writer
);
90 elem_writer
.AppendString("numeric");
91 elem_writer
.AppendVariantOfBool(numeric
);
92 sub_writer
.CloseContainer(&elem_writer
);
93 sub_writer
.OpenDictEntry(&elem_writer
);
94 elem_writer
.AppendString("v6");
95 elem_writer
.AppendVariantOfBool(ipv6
);
96 sub_writer
.CloseContainer(&elem_writer
);
97 writer
.CloseContainer(&sub_writer
);
98 debugdaemon_proxy_
->CallMethod(
100 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
101 base::Bind(&DebugDaemonClientImpl::OnGetRoutes
,
102 weak_ptr_factory_
.GetWeakPtr(),
106 virtual void GetNetworkStatus(const GetNetworkStatusCallback
& callback
)
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 virtual void GetModemStatus(const GetModemStatusCallback
& callback
)
120 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
121 debugd::kGetModemStatus
);
122 debugdaemon_proxy_
->CallMethod(
124 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
125 base::Bind(&DebugDaemonClientImpl::OnGetModemStatus
,
126 weak_ptr_factory_
.GetWeakPtr(),
130 virtual void GetWiMaxStatus(const GetWiMaxStatusCallback
& callback
)
132 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
133 debugd::kGetWiMaxStatus
);
134 debugdaemon_proxy_
->CallMethod(
136 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
137 base::Bind(&DebugDaemonClientImpl::OnGetWiMaxStatus
,
138 weak_ptr_factory_
.GetWeakPtr(),
142 virtual void GetNetworkInterfaces(
143 const GetNetworkInterfacesCallback
& callback
) OVERRIDE
{
144 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
145 debugd::kGetInterfaces
);
146 debugdaemon_proxy_
->CallMethod(
148 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
149 base::Bind(&DebugDaemonClientImpl::OnGetNetworkInterfaces
,
150 weak_ptr_factory_
.GetWeakPtr(),
154 virtual void GetPerfData(uint32_t duration
,
155 const GetPerfDataCallback
& callback
) OVERRIDE
{
156 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
157 debugd::kGetRichPerfData
);
158 dbus::MessageWriter
writer(&method_call
);
159 writer
.AppendUint32(duration
);
161 debugdaemon_proxy_
->CallMethod(
163 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
164 base::Bind(&DebugDaemonClientImpl::OnGetPerfData
,
165 weak_ptr_factory_
.GetWeakPtr(),
169 virtual void GetScrubbedLogs(const GetLogsCallback
& callback
) OVERRIDE
{
170 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
171 debugd::kGetFeedbackLogs
);
172 debugdaemon_proxy_
->CallMethod(
174 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
175 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
176 weak_ptr_factory_
.GetWeakPtr(),
180 virtual void GetAllLogs(const GetLogsCallback
& callback
)
182 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
183 debugd::kGetAllLogs
);
184 debugdaemon_proxy_
->CallMethod(
186 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
187 base::Bind(&DebugDaemonClientImpl::OnGetAllLogs
,
188 weak_ptr_factory_
.GetWeakPtr(),
192 virtual void GetUserLogFiles(
193 const GetLogsCallback
& callback
) OVERRIDE
{
194 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
195 debugd::kGetUserLogFiles
);
196 debugdaemon_proxy_
->CallMethod(
198 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
199 base::Bind(&DebugDaemonClientImpl::OnGetUserLogFiles
,
200 weak_ptr_factory_
.GetWeakPtr(),
204 virtual void StartSystemTracing() OVERRIDE
{
205 dbus::MethodCall
method_call(
206 debugd::kDebugdInterface
,
207 debugd::kSystraceStart
);
208 dbus::MessageWriter
writer(&method_call
);
209 writer
.AppendString("all"); // TODO(sleffler) parameterize category list
211 DVLOG(1) << "Requesting a systrace start";
212 debugdaemon_proxy_
->CallMethod(
214 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
215 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
216 weak_ptr_factory_
.GetWeakPtr()));
219 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback
&
221 if (pipe_reader_
!= NULL
) {
222 LOG(ERROR
) << "Busy doing StopSystemTracing";
226 scoped_refptr
<base::TaskRunner
> task_runner
=
227 base::WorkerPool::GetTaskRunner(true /* task_is_slow */);
229 pipe_reader_
.reset(new PipeReaderForString(
231 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
232 weak_ptr_factory_
.GetWeakPtr())));
234 base::File pipe_write_end
= pipe_reader_
->StartIO();
235 // Create dbus::FileDescriptor on the worker thread; on return we'll
236 // issue the D-Bus request to stop tracing and collect results.
237 base::PostTaskAndReplyWithResult(
241 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing
,
242 base::Passed(&pipe_write_end
)),
244 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem
,
245 weak_ptr_factory_
.GetWeakPtr(),
250 virtual void TestICMP(const std::string
& ip_address
,
251 const TestICMPCallback
& callback
) OVERRIDE
{
252 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
254 dbus::MessageWriter
writer(&method_call
);
255 writer
.AppendString(ip_address
);
256 debugdaemon_proxy_
->CallMethod(
258 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
259 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
260 weak_ptr_factory_
.GetWeakPtr(),
264 virtual void TestICMPWithOptions(
265 const std::string
& ip_address
,
266 const std::map
<std::string
, std::string
>& options
,
267 const TestICMPCallback
& callback
) OVERRIDE
{
268 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
269 debugd::kTestICMPWithOptions
);
270 dbus::MessageWriter
writer(&method_call
);
271 dbus::MessageWriter
sub_writer(NULL
);
272 dbus::MessageWriter
elem_writer(NULL
);
275 writer
.AppendString(ip_address
);
277 // Write the options.
278 writer
.OpenArray("{ss}", &sub_writer
);
279 std::map
<std::string
, std::string
>::const_iterator it
;
280 for (it
= options
.begin(); it
!= options
.end(); ++it
) {
281 sub_writer
.OpenDictEntry(&elem_writer
);
282 elem_writer
.AppendString(it
->first
);
283 elem_writer
.AppendString(it
->second
);
284 sub_writer
.CloseContainer(&elem_writer
);
286 writer
.CloseContainer(&sub_writer
);
288 // Call the function.
289 debugdaemon_proxy_
->CallMethod(
291 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
292 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
293 weak_ptr_factory_
.GetWeakPtr(),
297 virtual void UploadCrashes() OVERRIDE
{
298 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
299 debugd::kUploadCrashes
);
300 debugdaemon_proxy_
->CallMethod(
302 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
303 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
304 weak_ptr_factory_
.GetWeakPtr()));
308 virtual void Init(dbus::Bus
* bus
) OVERRIDE
{
310 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
311 dbus::ObjectPath(debugd::kDebugdServicePath
));
315 // Called when a CheckValidity response is received.
316 void OnCheckValidityGetDebugLogs(dbus::FileDescriptor
* file_descriptor
,
317 const GetDebugLogsCallback
& callback
) {
318 // Issue the dbus request to get debug logs.
319 dbus::MethodCall
method_call(
320 debugd::kDebugdInterface
,
321 debugd::kGetDebugLogs
);
322 dbus::MessageWriter
writer(&method_call
);
323 writer
.AppendFileDescriptor(*file_descriptor
);
325 debugdaemon_proxy_
->CallMethod(
327 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
328 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
329 weak_ptr_factory_
.GetWeakPtr(),
333 // Called when a response for GetDebugLogs() is received.
334 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
335 dbus::Response
* response
) {
337 LOG(ERROR
) << "Failed to get debug logs";
344 // Called when a response for SetDebugMode() is received.
345 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
346 dbus::Response
* response
) {
348 LOG(ERROR
) << "Failed to change debug mode";
355 void OnGetRoutes(const GetRoutesCallback
& callback
,
356 dbus::Response
* response
) {
357 std::vector
<std::string
> routes
;
359 dbus::MessageReader
reader(response
);
360 if (reader
.PopArrayOfStrings(&routes
)) {
361 callback
.Run(true, routes
);
363 LOG(ERROR
) << "Got non-array response from GetRoutes";
364 callback
.Run(false, routes
);
367 callback
.Run(false, routes
);
371 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
372 dbus::Response
* response
) {
374 if (response
&& dbus::MessageReader(response
).PopString(&status
))
375 callback
.Run(true, status
);
377 callback
.Run(false, "");
380 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
381 dbus::Response
* response
) {
383 if (response
&& dbus::MessageReader(response
).PopString(&status
))
384 callback
.Run(true, status
);
386 callback
.Run(false, "");
389 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
390 dbus::Response
* response
) {
392 if (response
&& dbus::MessageReader(response
).PopString(&status
))
393 callback
.Run(true, status
);
395 callback
.Run(false, "");
398 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
399 dbus::Response
* response
) {
401 if (response
&& dbus::MessageReader(response
).PopString(&status
))
402 callback
.Run(true, status
);
404 callback
.Run(false, "");
407 void OnGetPerfData(const GetPerfDataCallback
& callback
,
408 dbus::Response
* response
) {
409 std::vector
<uint8
> data
;
415 dbus::MessageReader
reader(response
);
416 const uint8
* buffer
= NULL
;
418 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
421 // TODO(asharif): Figure out a way to avoid this copy.
422 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
427 void OnGetAllLogs(const GetLogsCallback
& callback
,
428 dbus::Response
* response
) {
429 std::map
<std::string
, std::string
> logs
;
430 bool broken
= false; // did we see a broken (k,v) pair?
431 dbus::MessageReader
sub_reader(NULL
);
432 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
433 callback
.Run(false, logs
);
436 while (sub_reader
.HasMoreData()) {
437 dbus::MessageReader
sub_sub_reader(NULL
);
438 std::string key
, value
;
439 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
440 || !sub_sub_reader
.PopString(&key
)
441 || !sub_sub_reader
.PopString(&value
)) {
447 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
450 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
451 dbus::Response
* response
) {
452 return OnGetAllLogs(callback
, response
);
455 // Called when a response for a simple start is received.
456 void OnStartMethod(dbus::Response
* response
) {
458 LOG(ERROR
) << "Failed to request start";
463 // Creates dbus::FileDescriptor from base::File.
464 static scoped_ptr
<dbus::FileDescriptor
>
465 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end
) {
466 if (!pipe_write_end
.IsValid()) {
467 LOG(ERROR
) << "Cannot create pipe reader";
468 // NB: continue anyway to shutdown tracing; toss trace data
469 pipe_write_end
.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
470 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
471 // TODO(sleffler) if this fails AppendFileDescriptor will abort
473 scoped_ptr
<dbus::FileDescriptor
> file_descriptor(new dbus::FileDescriptor
);
474 file_descriptor
->PutValue(pipe_write_end
.TakePlatformFile());
475 file_descriptor
->CheckValidity();
476 return file_descriptor
.Pass();
479 // Called when a CheckValidity response is received.
480 void OnCreateFileDescriptorRequestStopSystem(
481 const StopSystemTracingCallback
& callback
,
482 scoped_ptr
<dbus::FileDescriptor
> file_descriptor
) {
483 DCHECK(file_descriptor
);
485 // Issue the dbus request to stop system tracing
486 dbus::MethodCall
method_call(
487 debugd::kDebugdInterface
,
488 debugd::kSystraceStop
);
489 dbus::MessageWriter
writer(&method_call
);
490 writer
.AppendFileDescriptor(*file_descriptor
);
492 callback_
= callback
;
494 DVLOG(1) << "Requesting a systrace stop";
495 debugdaemon_proxy_
->CallMethod(
497 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
498 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
499 weak_ptr_factory_
.GetWeakPtr()));
502 // Called when a response for RequestStopSystemTracing() is received.
503 void OnRequestStopSystemTracing(dbus::Response
* response
) {
505 LOG(ERROR
) << "Failed to request systrace stop";
506 // If debugd crashes or completes I/O before this message is processed
507 // then pipe_reader_ can be NULL, see OnIOComplete().
508 if (pipe_reader_
.get())
509 pipe_reader_
->OnDataReady(-1); // terminate data stream
511 // NB: requester is signaled when i/o completes
514 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
516 if (response
&& dbus::MessageReader(response
).PopString(&status
))
517 callback
.Run(true, status
);
519 callback
.Run(false, "");
522 // Called when pipe i/o completes; pass data on and delete the instance.
523 void OnIOComplete() {
524 std::string pipe_data
;
525 pipe_reader_
->GetData(&pipe_data
);
526 callback_
.Run(base::RefCountedString::TakeString(&pipe_data
));
527 pipe_reader_
.reset();
530 dbus::ObjectProxy
* debugdaemon_proxy_
;
531 scoped_ptr
<PipeReaderForString
> pipe_reader_
;
532 StopSystemTracingCallback callback_
;
533 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
535 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
538 DebugDaemonClient::DebugDaemonClient() {
541 DebugDaemonClient::~DebugDaemonClient() {
545 DebugDaemonClient::StopSystemTracingCallback
546 DebugDaemonClient::EmptyStopSystemTracingCallback() {
547 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
551 DebugDaemonClient
* DebugDaemonClient::Create() {
552 return new DebugDaemonClientImpl();
555 } // namespace chromeos