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 virtual ~DebugDaemonClientImpl() {}
47 // DebugDaemonClient override.
48 virtual 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 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(
220 scoped_refptr
<base::TaskRunner
> task_runner
,
221 const StopSystemTracingCallback
& callback
) OVERRIDE
{
222 if (pipe_reader_
!= NULL
) {
223 LOG(ERROR
) << "Busy doing StopSystemTracing";
227 pipe_reader_
.reset(new PipeReaderForString(
229 base::Bind(&DebugDaemonClientImpl::OnIOComplete
,
230 weak_ptr_factory_
.GetWeakPtr())));
232 base::File pipe_write_end
= pipe_reader_
->StartIO();
233 // Create dbus::FileDescriptor on the worker thread; on return we'll
234 // issue the D-Bus request to stop tracing and collect results.
235 base::PostTaskAndReplyWithResult(
239 &DebugDaemonClientImpl::CreateFileDescriptorToStopSystemTracing
,
240 base::Passed(&pipe_write_end
)),
242 &DebugDaemonClientImpl::OnCreateFileDescriptorRequestStopSystem
,
243 weak_ptr_factory_
.GetWeakPtr(),
248 virtual void TestICMP(const std::string
& ip_address
,
249 const TestICMPCallback
& callback
) OVERRIDE
{
250 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
252 dbus::MessageWriter
writer(&method_call
);
253 writer
.AppendString(ip_address
);
254 debugdaemon_proxy_
->CallMethod(
256 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
257 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
258 weak_ptr_factory_
.GetWeakPtr(),
262 virtual void TestICMPWithOptions(
263 const std::string
& ip_address
,
264 const std::map
<std::string
, std::string
>& options
,
265 const TestICMPCallback
& callback
) OVERRIDE
{
266 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
267 debugd::kTestICMPWithOptions
);
268 dbus::MessageWriter
writer(&method_call
);
269 dbus::MessageWriter
sub_writer(NULL
);
270 dbus::MessageWriter
elem_writer(NULL
);
273 writer
.AppendString(ip_address
);
275 // Write the options.
276 writer
.OpenArray("{ss}", &sub_writer
);
277 std::map
<std::string
, std::string
>::const_iterator it
;
278 for (it
= options
.begin(); it
!= options
.end(); ++it
) {
279 sub_writer
.OpenDictEntry(&elem_writer
);
280 elem_writer
.AppendString(it
->first
);
281 elem_writer
.AppendString(it
->second
);
282 sub_writer
.CloseContainer(&elem_writer
);
284 writer
.CloseContainer(&sub_writer
);
286 // Call the function.
287 debugdaemon_proxy_
->CallMethod(
289 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
290 base::Bind(&DebugDaemonClientImpl::OnTestICMP
,
291 weak_ptr_factory_
.GetWeakPtr(),
295 virtual void UploadCrashes() OVERRIDE
{
296 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
297 debugd::kUploadCrashes
);
298 debugdaemon_proxy_
->CallMethod(
300 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
301 base::Bind(&DebugDaemonClientImpl::OnStartMethod
,
302 weak_ptr_factory_
.GetWeakPtr()));
306 virtual void Init(dbus::Bus
* bus
) OVERRIDE
{
308 bus
->GetObjectProxy(debugd::kDebugdServiceName
,
309 dbus::ObjectPath(debugd::kDebugdServicePath
));
313 // Called when a CheckValidity response is received.
314 void OnCheckValidityGetDebugLogs(bool is_compressed
,
315 dbus::FileDescriptor
* file_descriptor
,
316 const GetDebugLogsCallback
& callback
) {
317 // Issue the dbus request to get debug logs.
318 dbus::MethodCall
method_call(debugd::kDebugdInterface
,
319 debugd::kDumpDebugLogs
);
320 dbus::MessageWriter
writer(&method_call
);
321 writer
.AppendBool(is_compressed
);
322 writer
.AppendFileDescriptor(*file_descriptor
);
324 debugdaemon_proxy_
->CallMethod(
326 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
327 base::Bind(&DebugDaemonClientImpl::OnGetDebugLogs
,
328 weak_ptr_factory_
.GetWeakPtr(),
332 // Called when a response for GetDebugLogs() is received.
333 void OnGetDebugLogs(const GetDebugLogsCallback
& callback
,
334 dbus::Response
* response
) {
336 LOG(ERROR
) << "Failed to get debug logs";
343 // Called when a response for SetDebugMode() is received.
344 void OnSetDebugMode(const SetDebugModeCallback
& callback
,
345 dbus::Response
* response
) {
347 LOG(ERROR
) << "Failed to change debug mode";
354 void OnGetRoutes(const GetRoutesCallback
& callback
,
355 dbus::Response
* response
) {
356 std::vector
<std::string
> routes
;
358 dbus::MessageReader
reader(response
);
359 if (reader
.PopArrayOfStrings(&routes
)) {
360 callback
.Run(true, routes
);
362 LOG(ERROR
) << "Got non-array response from GetRoutes";
363 callback
.Run(false, routes
);
366 callback
.Run(false, routes
);
370 void OnGetNetworkStatus(const GetNetworkStatusCallback
& callback
,
371 dbus::Response
* response
) {
373 if (response
&& dbus::MessageReader(response
).PopString(&status
))
374 callback
.Run(true, status
);
376 callback
.Run(false, "");
379 void OnGetModemStatus(const GetModemStatusCallback
& callback
,
380 dbus::Response
* response
) {
382 if (response
&& dbus::MessageReader(response
).PopString(&status
))
383 callback
.Run(true, status
);
385 callback
.Run(false, "");
388 void OnGetWiMaxStatus(const GetWiMaxStatusCallback
& callback
,
389 dbus::Response
* response
) {
391 if (response
&& dbus::MessageReader(response
).PopString(&status
))
392 callback
.Run(true, status
);
394 callback
.Run(false, "");
397 void OnGetNetworkInterfaces(const GetNetworkInterfacesCallback
& callback
,
398 dbus::Response
* response
) {
400 if (response
&& dbus::MessageReader(response
).PopString(&status
))
401 callback
.Run(true, status
);
403 callback
.Run(false, "");
406 void OnGetPerfData(const GetPerfDataCallback
& callback
,
407 dbus::Response
* response
) {
408 std::vector
<uint8
> data
;
414 dbus::MessageReader
reader(response
);
415 const uint8
* buffer
= NULL
;
417 if (!reader
.PopArrayOfBytes(&buffer
, &buf_size
))
420 // TODO(asharif): Figure out a way to avoid this copy.
421 data
.insert(data
.end(), buffer
, buffer
+ buf_size
);
426 void OnGetAllLogs(const GetLogsCallback
& callback
,
427 dbus::Response
* response
) {
428 std::map
<std::string
, std::string
> logs
;
429 bool broken
= false; // did we see a broken (k,v) pair?
430 dbus::MessageReader
sub_reader(NULL
);
431 if (!response
|| !dbus::MessageReader(response
).PopArray(&sub_reader
)) {
432 callback
.Run(false, logs
);
435 while (sub_reader
.HasMoreData()) {
436 dbus::MessageReader
sub_sub_reader(NULL
);
437 std::string key
, value
;
438 if (!sub_reader
.PopDictEntry(&sub_sub_reader
)
439 || !sub_sub_reader
.PopString(&key
)
440 || !sub_sub_reader
.PopString(&value
)) {
446 callback
.Run(!sub_reader
.HasMoreData() && !broken
, logs
);
449 void OnGetUserLogFiles(const GetLogsCallback
& callback
,
450 dbus::Response
* response
) {
451 return OnGetAllLogs(callback
, response
);
454 // Called when a response for a simple start is received.
455 void OnStartMethod(dbus::Response
* response
) {
457 LOG(ERROR
) << "Failed to request start";
462 // Creates dbus::FileDescriptor from base::File.
463 static scoped_ptr
<dbus::FileDescriptor
>
464 CreateFileDescriptorToStopSystemTracing(base::File pipe_write_end
) {
465 if (!pipe_write_end
.IsValid()) {
466 LOG(ERROR
) << "Cannot create pipe reader";
467 // NB: continue anyway to shutdown tracing; toss trace data
468 pipe_write_end
.Initialize(base::FilePath(FILE_PATH_LITERAL("/dev/null")),
469 base::File::FLAG_OPEN
| base::File::FLAG_WRITE
);
470 // TODO(sleffler) if this fails AppendFileDescriptor will abort
472 scoped_ptr
<dbus::FileDescriptor
> file_descriptor(new dbus::FileDescriptor
);
473 file_descriptor
->PutValue(pipe_write_end
.TakePlatformFile());
474 file_descriptor
->CheckValidity();
475 return file_descriptor
.Pass();
478 // Called when a CheckValidity response is received.
479 void OnCreateFileDescriptorRequestStopSystem(
480 const StopSystemTracingCallback
& callback
,
481 scoped_ptr
<dbus::FileDescriptor
> file_descriptor
) {
482 DCHECK(file_descriptor
);
484 // Issue the dbus request to stop system tracing
485 dbus::MethodCall
method_call(
486 debugd::kDebugdInterface
,
487 debugd::kSystraceStop
);
488 dbus::MessageWriter
writer(&method_call
);
489 writer
.AppendFileDescriptor(*file_descriptor
);
491 callback_
= callback
;
493 DVLOG(1) << "Requesting a systrace stop";
494 debugdaemon_proxy_
->CallMethod(
496 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT
,
497 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing
,
498 weak_ptr_factory_
.GetWeakPtr()));
501 // Called when a response for RequestStopSystemTracing() is received.
502 void OnRequestStopSystemTracing(dbus::Response
* response
) {
504 LOG(ERROR
) << "Failed to request systrace stop";
505 // If debugd crashes or completes I/O before this message is processed
506 // then pipe_reader_ can be NULL, see OnIOComplete().
507 if (pipe_reader_
.get())
508 pipe_reader_
->OnDataReady(-1); // terminate data stream
510 // NB: requester is signaled when i/o completes
513 void OnTestICMP(const TestICMPCallback
& callback
, dbus::Response
* response
) {
515 if (response
&& dbus::MessageReader(response
).PopString(&status
))
516 callback
.Run(true, status
);
518 callback
.Run(false, "");
521 // Called when pipe i/o completes; pass data on and delete the instance.
522 void OnIOComplete() {
523 std::string pipe_data
;
524 pipe_reader_
->GetData(&pipe_data
);
525 callback_
.Run(base::RefCountedString::TakeString(&pipe_data
));
526 pipe_reader_
.reset();
529 dbus::ObjectProxy
* debugdaemon_proxy_
;
530 scoped_ptr
<PipeReaderForString
> pipe_reader_
;
531 StopSystemTracingCallback callback_
;
532 base::WeakPtrFactory
<DebugDaemonClientImpl
> weak_ptr_factory_
;
534 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl
);
537 DebugDaemonClient::DebugDaemonClient() {
540 DebugDaemonClient::~DebugDaemonClient() {
544 DebugDaemonClient::StopSystemTracingCallback
545 DebugDaemonClient::EmptyStopSystemTracingCallback() {
546 return base::Bind(&EmptyStopSystemTracingCallbackBody
);
550 DebugDaemonClient
* DebugDaemonClient::Create() {
551 return new DebugDaemonClientImpl();
554 } // namespace chromeos