OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include <fcntl.h> | |
6 #include <unistd.h> | |
7 | |
8 #include "chrome/browser/chromeos/dbus/debug_daemon_client.h" | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/callback.h" | |
12 #include "base/chromeos/chromeos_version.h" | |
13 #include "base/eintr_wrapper.h" | |
14 #include "base/memory/ref_counted_memory.h" | |
15 #include "base/platform_file.h" | |
16 #include "base/string_util.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "dbus/bus.h" | |
19 #include "dbus/message.h" | |
20 #include "dbus/object_path.h" | |
21 #include "dbus/object_proxy.h" | |
22 #include "net/base/file_stream.h" | |
23 #include "net/base/io_buffer.h" | |
24 #include "net/base/net_errors.h" | |
25 #include "third_party/cros_system_api/dbus/service_constants.h" | |
26 | |
27 using content::BrowserThread; | |
28 | |
29 namespace { | |
30 | |
31 // Used in DebugDaemonClient::EmptySystemStopTracingCallback(). | |
32 void EmptyStopSystemTracingCallbackBody( | |
33 const scoped_refptr<base::RefCountedString>& unused_result) { | |
34 } | |
35 | |
36 // Simple class to encapsulate collecting data from a pipe into a | |
37 // string. To use, instantiate the class, start i/o, and then delete | |
38 // the instance on callback. The data should be retrieved before | |
39 // delete and extracted or copied. | |
40 // | |
41 // TODO(sleffler) move data collection to a sub-class so this | |
42 // can be reused to process data as it is received | |
43 class PipeReader { | |
44 public: | |
45 typedef base::Callback<void(void)>IOCompleteCallback; | |
46 | |
47 explicit PipeReader(IOCompleteCallback callback) | |
48 : data_stream_(NULL), | |
49 io_buffer_(new net::IOBufferWithSize(4096)), | |
50 weak_ptr_factory_(this), | |
51 callback_(callback) { | |
52 pipe_fd_[0] = pipe_fd_[1] = -1; | |
53 } | |
54 | |
55 virtual ~PipeReader() { | |
56 if (pipe_fd_[0] != -1) | |
57 if (HANDLE_EINTR(close(pipe_fd_[0])) < 0) | |
58 PLOG(ERROR) << "close[0]"; | |
59 if (pipe_fd_[1] != -1) | |
60 if (HANDLE_EINTR(close(pipe_fd_[1])) < 0) | |
61 PLOG(ERROR) << "close[1]"; | |
62 } | |
63 | |
64 // Returns descriptor for the writeable side of the pipe. | |
65 int GetWriteFD() { return pipe_fd_[1]; } | |
66 | |
67 // Closes writeable descriptor; normally used in parent process after fork. | |
68 void CloseWriteFD() { | |
69 if (pipe_fd_[1] != -1) { | |
70 if (HANDLE_EINTR(close(pipe_fd_[1])) < 0) | |
71 PLOG(ERROR) << "close"; | |
72 pipe_fd_[1] = -1; | |
73 } | |
74 } | |
75 | |
76 // Returns collected data. | |
77 std::string* data() { return &data_; } | |
78 | |
79 // Starts data collection. Returns true if stream was setup correctly. | |
80 // On success data will automatically be accumulated into a string that | |
81 // can be retrieved with PipeReader::data(). To shutdown collection delete | |
82 // the instance and/or use PipeReader::OnDataReady(-1). | |
83 bool StartIO() { | |
84 // Use a pipe to collect data | |
85 const int status = HANDLE_EINTR(pipe(pipe_fd_)); | |
86 if (status < 0) { | |
87 PLOG(ERROR) << "pipe"; | |
88 return false; | |
89 } | |
90 base::PlatformFile data_file_ = pipe_fd_[0]; // read side | |
91 data_stream_.reset(new net::FileStream(data_file_, | |
92 base::PLATFORM_FILE_READ | base::PLATFORM_FILE_ASYNC, | |
93 NULL)); | |
94 | |
95 // Post an initial async read to setup data collection | |
96 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(), | |
97 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr())); | |
98 if (rv != net::ERR_IO_PENDING) { | |
99 LOG(ERROR) << "Unable to post initial read"; | |
100 return false; | |
101 } | |
102 return true; | |
103 } | |
104 | |
105 // Called when pipe data are available. Can also be used to shutdown | |
106 // data collection by passing -1 for |byte_count|. | |
107 void OnDataReady(int byte_count) { | |
108 DVLOG(1) << "OnDataReady byte_count " << byte_count; | |
109 if (byte_count <= 0) { | |
110 callback_.Run(); // signal creator to take data and delete us | |
111 return; | |
112 } | |
113 data_.append(io_buffer_->data(), byte_count); | |
114 | |
115 // Post another read | |
116 int rv = data_stream_->Read(io_buffer_.get(), io_buffer_->size(), | |
117 base::Bind(&PipeReader::OnDataReady, weak_ptr_factory_.GetWeakPtr())); | |
118 if (rv != net::ERR_IO_PENDING) { | |
119 LOG(ERROR) << "Unable to post another read"; | |
120 // TODO(sleffler) do something more intelligent? | |
121 } | |
122 } | |
123 | |
124 private: | |
125 friend class base::RefCounted<PipeReader>; | |
126 | |
127 int pipe_fd_[2]; | |
128 scoped_ptr<net::FileStream> data_stream_; | |
129 scoped_refptr<net::IOBufferWithSize> io_buffer_; | |
130 base::WeakPtrFactory<PipeReader> weak_ptr_factory_; | |
131 std::string data_; | |
132 IOCompleteCallback callback_; | |
133 | |
134 DISALLOW_COPY_AND_ASSIGN(PipeReader); | |
135 }; | |
136 | |
137 } // namespace | |
138 | |
139 namespace chromeos { | |
140 | |
141 // The DebugDaemonClient implementation used in production. | |
142 class DebugDaemonClientImpl : public DebugDaemonClient { | |
143 public: | |
144 explicit DebugDaemonClientImpl(dbus::Bus* bus) | |
145 : debugdaemon_proxy_(NULL), | |
146 weak_ptr_factory_(this), | |
147 pipe_reader_(NULL) { | |
148 debugdaemon_proxy_ = bus->GetObjectProxy( | |
149 debugd::kDebugdServiceName, | |
150 dbus::ObjectPath(debugd::kDebugdServicePath)); | |
151 } | |
152 | |
153 virtual ~DebugDaemonClientImpl() {} | |
154 | |
155 // DebugDaemonClient override. | |
156 virtual void StartSystemTracing() OVERRIDE { | |
157 dbus::MethodCall method_call( | |
158 debugd::kDebugdInterface, | |
159 debugd::kSystraceStart); | |
160 dbus::MessageWriter writer(&method_call); | |
161 writer.AppendString("all"); // TODO(sleffler) parameterize category list | |
162 | |
163 DVLOG(1) << "Requesting a systrace start"; | |
164 debugdaemon_proxy_->CallMethod( | |
165 &method_call, | |
166 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
167 base::Bind(&DebugDaemonClientImpl::OnStartSystemTracing, | |
168 weak_ptr_factory_.GetWeakPtr())); | |
169 } | |
170 | |
171 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback& | |
172 callback) OVERRIDE { | |
173 if (pipe_reader_ != NULL) { | |
174 LOG(ERROR) << "Busy doing StopSystemTracing"; | |
175 return false; | |
176 } | |
177 | |
178 pipe_reader_.reset(new PipeReader( | |
179 base::Bind(&DebugDaemonClientImpl::OnIOComplete, | |
180 weak_ptr_factory_.GetWeakPtr()))); | |
181 int write_fd = -1; | |
182 if (!pipe_reader_->StartIO()) { | |
183 LOG(ERROR) << "Cannot create pipe reader"; | |
184 // NB: continue anyway to shutdown tracing; toss trace data | |
185 write_fd = HANDLE_EINTR(open("/dev/null", O_WRONLY)); | |
186 // TODO(sleffler) if this fails AppendFileDescriptor will abort | |
187 } else { | |
188 write_fd = pipe_reader_->GetWriteFD(); | |
189 } | |
190 | |
191 DCHECK(callback.is_null()); | |
192 callback_ = callback; | |
193 | |
194 // Issue the dbus request to stop system tracing | |
195 dbus::MethodCall method_call( | |
196 debugd::kDebugdInterface, | |
197 debugd::kSystraceStop); | |
198 dbus::MessageWriter writer(&method_call); | |
199 dbus::FileDescriptor temp(write_fd); // NB: explicit temp for C++98 | |
200 writer.AppendFileDescriptor(temp); | |
201 | |
202 DVLOG(1) << "Requesting a systrace stop"; | |
203 debugdaemon_proxy_->CallMethod( | |
204 &method_call, | |
205 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | |
206 base::Bind(&DebugDaemonClientImpl::OnRequestStopSystemTracing, | |
207 weak_ptr_factory_.GetWeakPtr())); | |
208 | |
209 pipe_reader_->CloseWriteFD(); // close our copy of fd after send | |
210 | |
211 return true; | |
212 } | |
213 | |
214 private: | |
215 // Called when a response for StartSystemTracing() is received. | |
216 void OnStartSystemTracing(dbus::Response* response) { | |
217 if (!response) { | |
218 LOG(ERROR) << "Failed to request systrace start"; | |
219 return; | |
220 } | |
221 } | |
222 | |
223 // Called when a response for RequestStopSystemTracing() is received. | |
224 void OnRequestStopSystemTracing(dbus::Response* response) { | |
225 if (!response) { | |
226 LOG(ERROR) << "Failed to request systrace stop"; | |
227 pipe_reader_->OnDataReady(-1); // terminate data stream | |
228 } | |
229 // NB: requester is signaled when i/o completes | |
230 } | |
231 | |
232 // Called when pipe i/o completes; pass data on and delete the instance. | |
233 void OnIOComplete() { | |
234 callback_.Run(base::RefCountedString::TakeString(pipe_reader_->data())); | |
235 pipe_reader_.reset(); | |
236 } | |
237 | |
238 dbus::ObjectProxy* debugdaemon_proxy_; | |
239 base::WeakPtrFactory<DebugDaemonClientImpl> weak_ptr_factory_; | |
240 scoped_ptr<PipeReader> pipe_reader_; | |
241 StopSystemTracingCallback callback_; | |
242 | |
243 DISALLOW_COPY_AND_ASSIGN(DebugDaemonClientImpl); | |
244 }; | |
245 | |
246 // The DebugDaemonClient implementation used on Linux desktop, | |
247 // which does nothing. | |
248 class DebugDaemonClientStubImpl : public DebugDaemonClient { | |
249 // DebugDaemonClient overrides. | |
250 virtual void StartSystemTracing() OVERRIDE {} | |
251 virtual bool RequestStopSystemTracing(const StopSystemTracingCallback& | |
252 callback) OVERRIDE { | |
253 std::string no_data; | |
254 callback.Run(base::RefCountedString::TakeString(&no_data)); | |
255 return true; | |
256 } | |
257 }; | |
258 | |
259 DebugDaemonClient::DebugDaemonClient() { | |
260 } | |
261 | |
262 DebugDaemonClient::~DebugDaemonClient() { | |
263 } | |
264 | |
265 // static | |
266 DebugDaemonClient::StopSystemTracingCallback | |
267 DebugDaemonClient::EmptyStopSystemTracingCallback() { | |
268 return base::Bind(&EmptyStopSystemTracingCallbackBody); | |
269 } | |
270 | |
271 // static | |
272 DebugDaemonClient* DebugDaemonClient::Create(DBusClientImplementationType type, | |
273 dbus::Bus* bus) { | |
274 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) | |
275 return new DebugDaemonClientImpl(bus); | |
276 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type); | |
277 return new DebugDaemonClientStubImpl(); | |
278 } | |
279 | |
280 } // namespace chromeos | |
OLD | NEW |