| 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 |