Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
| 4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
| 5 */ | 5 */ |
| 6 | 6 |
| 7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" | 7 #define NACL_LOG_MODULE_NAME "Plugin::ServiceRuntime" |
| 8 | 8 |
| 9 #include "native_client/src/trusted/plugin/service_runtime.h" | 9 #include "native_client/src/trusted/plugin/service_runtime.h" |
| 10 | 10 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 39 // PostMessage method, so this undef must appear before any of those. | 39 // PostMessage method, so this undef must appear before any of those. |
| 40 #ifdef PostMessage | 40 #ifdef PostMessage |
| 41 #undef PostMessage | 41 #undef PostMessage |
| 42 #endif | 42 #endif |
| 43 #include "native_client/src/trusted/plugin/plugin.h" | 43 #include "native_client/src/trusted/plugin/plugin.h" |
| 44 #include "native_client/src/trusted/plugin/plugin_error.h" | 44 #include "native_client/src/trusted/plugin/plugin_error.h" |
| 45 #include "native_client/src/trusted/plugin/pnacl_coordinator.h" | 45 #include "native_client/src/trusted/plugin/pnacl_coordinator.h" |
| 46 #include "native_client/src/trusted/plugin/pnacl_resources.h" | 46 #include "native_client/src/trusted/plugin/pnacl_resources.h" |
| 47 #include "native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" | 47 #include "native_client/src/trusted/plugin/sel_ldr_launcher_chrome.h" |
| 48 #include "native_client/src/trusted/plugin/srpc_client.h" | 48 #include "native_client/src/trusted/plugin/srpc_client.h" |
| 49 | 49 #include "native_client/src/trusted/reverse_service/nacl_file_info.h" |
| 50 #include "native_client/src/trusted/weak_ref/call_on_main_thread.h" | |
| 51 | |
| 52 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" | 50 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" |
| 53 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" | 51 #include "native_client/src/trusted/service_runtime/include/sys/nacl_imc_api.h" |
| 52 #include "native_client/src/trusted/weak_ref/call_on_main_thread.h" | |
| 54 | 53 |
| 55 #include "ppapi/c/pp_errors.h" | 54 #include "ppapi/c/pp_errors.h" |
| 56 #include "ppapi/c/trusted/ppb_file_io_trusted.h" | 55 #include "ppapi/c/trusted/ppb_file_io_trusted.h" |
| 57 #include "ppapi/cpp/core.h" | 56 #include "ppapi/cpp/core.h" |
| 58 #include "ppapi/cpp/completion_callback.h" | 57 #include "ppapi/cpp/completion_callback.h" |
| 59 #include "ppapi/cpp/file_io.h" | 58 #include "ppapi/cpp/file_io.h" |
| 60 | 59 |
| 61 namespace { | 60 namespace { |
| 62 | 61 |
| 63 // For doing crude quota enforcement on writes to temp files. | 62 // For doing crude quota enforcement on writes to temp files. |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 return false; | 164 return false; |
| 166 } | 165 } |
| 167 | 166 |
| 168 return true; | 167 return true; |
| 169 } | 168 } |
| 170 | 169 |
| 171 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey | 170 // TODO(bsy): OpenManifestEntry should use the manifest to ResolveKey |
| 172 // and invoke StreamAsFile with a completion callback that invokes | 171 // and invoke StreamAsFile with a completion callback that invokes |
| 173 // GetPOSIXFileDesc. | 172 // GetPOSIXFileDesc. |
| 174 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, | 173 bool PluginReverseInterface::OpenManifestEntry(nacl::string url_key, |
| 175 int32_t* out_desc) { | 174 struct NaClFileInfo *info) { |
| 176 ErrorInfo error_info; | 175 ErrorInfo error_info; |
| 177 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! | 176 bool op_complete = false; // NB: mu_ and cv_ also controls access to this! |
| 177 // The to_open object is owned by the weak ref callback. Because this function | |
| 178 // waits for the callback to finish, the to_open object will be deallocated on | |
| 179 // the main thread before this function can return. The pointers it contains | |
| 180 // to stack variables will not leak. | |
| 178 OpenManifestEntryResource* to_open = | 181 OpenManifestEntryResource* to_open = |
| 179 new OpenManifestEntryResource(url_key, out_desc, | 182 new OpenManifestEntryResource(url_key, info, |
| 180 &error_info, &op_complete); | 183 &error_info, &op_complete); |
| 181 CHECK(to_open != NULL); | 184 CHECK(to_open != NULL); |
| 182 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", | 185 NaClLog(4, "PluginReverseInterface::OpenManifestEntry: %s\n", |
| 183 url_key.c_str()); | 186 url_key.c_str()); |
| 184 // This assumes we are not on the main thread. If false, we deadlock. | 187 // This assumes we are not on the main thread. If false, we deadlock. |
| 185 plugin::WeakRefCallOnMainThread( | 188 plugin::WeakRefCallOnMainThread( |
| 186 anchor_, | 189 anchor_, |
| 187 0, | 190 0, |
| 188 this, | 191 this, |
| 189 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, | 192 &plugin::PluginReverseInterface::OpenManifestEntry_MainThreadContinuation, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 // The caller is responsible for not closing *out_desc. If it is | 224 // The caller is responsible for not closing *out_desc. If it is |
| 222 // closed prematurely, then another open could re-use the OS | 225 // closed prematurely, then another open could re-use the OS |
| 223 // descriptor, confusing the opened_ map. If the caller is going to | 226 // descriptor, confusing the opened_ map. If the caller is going to |
| 224 // want to make a NaClDesc object and transfer it etc., then the | 227 // want to make a NaClDesc object and transfer it etc., then the |
| 225 // caller should DUP the descriptor (but remember the original | 228 // caller should DUP the descriptor (but remember the original |
| 226 // value) for use by the NaClDesc object, which closes when the | 229 // value) for use by the NaClDesc object, which closes when the |
| 227 // object is destroyed. | 230 // object is destroyed. |
| 228 NaClLog(4, | 231 NaClLog(4, |
| 229 "PluginReverseInterface::OpenManifestEntry:" | 232 "PluginReverseInterface::OpenManifestEntry:" |
| 230 " *out_desc = %d\n", | 233 " *out_desc = %d\n", |
| 231 *out_desc); | 234 info->desc); |
| 232 if (*out_desc == -1) { | 235 if (info->desc == -1) { |
| 233 // TODO(bsy,ncbray): what else should we do with the error? This | 236 // TODO(bsy,ncbray): what else should we do with the error? This |
| 234 // is a runtime error that may simply be a programming error in | 237 // is a runtime error that may simply be a programming error in |
| 235 // the untrusted code, or it may be something else wrong w/ the | 238 // the untrusted code, or it may be something else wrong w/ the |
| 236 // manifest. | 239 // manifest. |
| 237 NaClLog(4, | 240 NaClLog(4, |
| 238 "OpenManifestEntry: failed for key %s, code %d (%s)\n", | 241 "OpenManifestEntry: failed for key %s, code %d (%s)\n", |
| 239 url_key.c_str(), | 242 url_key.c_str(), |
| 240 error_info.error_code(), | 243 error_info.error_code(), |
| 241 error_info.message().c_str()); | 244 error_info.message().c_str()); |
| 242 } | 245 } |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 260 | 263 |
| 261 std::string mapped_url; | 264 std::string mapped_url; |
| 262 PnaclOptions pnacl_options; | 265 PnaclOptions pnacl_options; |
| 263 if (!manifest_->ResolveKey(p->url, &mapped_url, | 266 if (!manifest_->ResolveKey(p->url, &mapped_url, |
| 264 &pnacl_options, p->error_info)) { | 267 &pnacl_options, p->error_info)) { |
| 265 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); | 268 NaClLog(4, "OpenManifestEntry_MainThreadContinuation: ResolveKey failed\n"); |
| 266 // Failed, and error_info has the details on what happened. Wake | 269 // Failed, and error_info has the details on what happened. Wake |
| 267 // up requesting thread -- we are done. | 270 // up requesting thread -- we are done. |
| 268 nacl::MutexLocker take(&mu_); | 271 nacl::MutexLocker take(&mu_); |
| 269 *p->op_complete_ptr = true; // done... | 272 *p->op_complete_ptr = true; // done... |
| 270 *p->out_desc = -1; // but failed. | 273 p->file_info->desc = -1; // but failed. |
| 271 NaClXCondVarBroadcast(&cv_); | 274 NaClXCondVarBroadcast(&cv_); |
| 272 return; | 275 return; |
| 273 } | 276 } |
| 274 NaClLog(4, | 277 NaClLog(4, |
| 275 "OpenManifestEntry_MainThreadContinuation: " | 278 "OpenManifestEntry_MainThreadContinuation: " |
| 276 "ResolveKey: %s -> %s (pnacl_translate(%d))\n", | 279 "ResolveKey: %s -> %s (pnacl_translate(%d))\n", |
| 277 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate()); | 280 p->url.c_str(), mapped_url.c_str(), pnacl_options.translate()); |
| 278 | 281 |
| 279 open_cont = new OpenManifestEntryResource(*p); // copy ctor! | 282 open_cont = new OpenManifestEntryResource(*p); // copy ctor! |
| 280 CHECK(open_cont != NULL); | 283 CHECK(open_cont != NULL); |
| 281 open_cont->url = mapped_url; | 284 open_cont->url = mapped_url; |
| 282 if (!pnacl_options.translate()) { | 285 if (!pnacl_options.translate()) { |
| 283 pp::CompletionCallback stream_cc = WeakRefNewCallback( | 286 pp::CompletionCallback stream_cc = WeakRefNewCallback( |
| 284 anchor_, | 287 anchor_, |
| 285 this, | 288 this, |
| 286 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, | 289 &PluginReverseInterface::StreamAsFile_MainThreadContinuation, |
| 287 open_cont); | 290 open_cont); |
| 288 // Normal files. | 291 // Normal files. |
| 289 if (!PnaclUrls::IsPnaclComponent(mapped_url)) { | 292 if (!PnaclUrls::IsPnaclComponent(mapped_url)) { |
| 290 if (!plugin_->StreamAsFile(mapped_url, | 293 if (!plugin_->StreamAsFile(mapped_url, |
| 291 stream_cc.pp_completion_callback())) { | 294 stream_cc.pp_completion_callback())) { |
| 292 NaClLog(4, | 295 NaClLog(4, |
| 293 "OpenManifestEntry_MainThreadContinuation: " | 296 "OpenManifestEntry_MainThreadContinuation: " |
| 294 "StreamAsFile failed\n"); | 297 "StreamAsFile failed\n"); |
| 295 nacl::MutexLocker take(&mu_); | 298 nacl::MutexLocker take(&mu_); |
| 296 *p->op_complete_ptr = true; // done... | 299 *p->op_complete_ptr = true; // done... |
| 297 *p->out_desc = -1; // but failed. | 300 p->file_info->desc = -1; // but failed. |
| 298 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 301 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 299 "ServiceRuntime: StreamAsFile failed"); | 302 "ServiceRuntime: StreamAsFile failed"); |
| 300 NaClXCondVarBroadcast(&cv_); | 303 NaClXCondVarBroadcast(&cv_); |
| 301 return; | 304 return; |
| 302 } | 305 } |
| 303 NaClLog(4, | 306 NaClLog(4, |
| 304 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); | 307 "OpenManifestEntry_MainThreadContinuation: StreamAsFile okay\n"); |
| 305 } else { | 308 } else { |
| 306 // Special PNaCl support files, that are installed on the | 309 // Special PNaCl support files, that are installed on the |
| 307 // user machine. | 310 // user machine. |
| 308 int32_t fd = PnaclResources::GetPnaclFD( | 311 int32_t fd = PnaclResources::GetPnaclFD( |
| 309 plugin_, | 312 plugin_, |
| 310 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str()); | 313 PnaclUrls::PnaclComponentURLToFilename(mapped_url).c_str()); |
| 311 if (fd < 0) { | 314 if (fd < 0) { |
| 312 // We should check earlier if the pnacl component wasn't installed | 315 // We should check earlier if the pnacl component wasn't installed |
| 313 // yet. At this point, we can't do much anymore, so just continue | 316 // yet. At this point, we can't do much anymore, so just continue |
| 314 // with an invalid fd. | 317 // with an invalid fd. |
| 315 NaClLog(4, | 318 NaClLog(4, |
| 316 "OpenManifestEntry_MainThreadContinuation: " | 319 "OpenManifestEntry_MainThreadContinuation: " |
| 317 "GetReadonlyPnaclFd failed\n"); | 320 "GetReadonlyPnaclFd failed\n"); |
| 318 // TODO(jvoung): Separate the error codes? | 321 // TODO(jvoung): Separate the error codes? |
| 319 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 322 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 320 "ServiceRuntime: GetPnaclFd failed"); | 323 "ServiceRuntime: GetPnaclFd failed"); |
| 321 } | 324 } |
| 322 nacl::MutexLocker take(&mu_); | 325 nacl::MutexLocker take(&mu_); |
| 323 *p->op_complete_ptr = true; // done! | 326 *p->op_complete_ptr = true; // done! |
| 324 *p->out_desc = fd; | 327 // TODO(ncbray): more info for faster validation? |
|
Mark Seaborn
2013/05/16 23:01:47
I'm not sure what this TODO means
Nick Bray (chromium)
2013/05/21 20:09:06
Done.
| |
| 328 p->file_info->desc = fd; | |
| 325 NaClXCondVarBroadcast(&cv_); | 329 NaClXCondVarBroadcast(&cv_); |
| 326 NaClLog(4, | 330 NaClLog(4, |
| 327 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n"); | 331 "OpenManifestEntry_MainThreadContinuation: GetPnaclFd okay\n"); |
| 328 } | 332 } |
| 329 } else { | 333 } else { |
| 330 // Requires PNaCl translation. | 334 // Requires PNaCl translation. |
| 331 NaClLog(4, | 335 NaClLog(4, |
| 332 "OpenManifestEntry_MainThreadContinuation: " | 336 "OpenManifestEntry_MainThreadContinuation: " |
| 333 "pulling down and translating.\n"); | 337 "pulling down and translating.\n"); |
| 334 if (plugin_->nacl_interface()->IsPnaclEnabled()) { | 338 if (plugin_->nacl_interface()->IsPnaclEnabled()) { |
| 335 pp::CompletionCallback translate_callback = | 339 pp::CompletionCallback translate_callback = |
| 336 WeakRefNewCallback( | 340 WeakRefNewCallback( |
| 337 anchor_, | 341 anchor_, |
| 338 this, | 342 this, |
| 339 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation, | 343 &PluginReverseInterface::BitcodeTranslate_MainThreadContinuation, |
| 340 open_cont); | 344 open_cont); |
| 341 // Will always call the callback on success or failure. | 345 // Will always call the callback on success or failure. |
| 342 pnacl_coordinator_.reset( | 346 pnacl_coordinator_.reset( |
| 343 PnaclCoordinator::BitcodeToNative(plugin_, | 347 PnaclCoordinator::BitcodeToNative(plugin_, |
| 344 mapped_url, | 348 mapped_url, |
| 345 pnacl_options, | 349 pnacl_options, |
| 346 translate_callback)); | 350 translate_callback)); |
| 347 } else { | 351 } else { |
| 348 nacl::MutexLocker take(&mu_); | 352 nacl::MutexLocker take(&mu_); |
| 349 *p->op_complete_ptr = true; // done... | 353 *p->op_complete_ptr = true; // done... |
| 350 *p->out_desc = -1; // but failed. | 354 p->file_info->desc = -1; // but failed. |
| 351 p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED, | 355 p->error_info->SetReport(ERROR_PNACL_NOT_ENABLED, |
| 352 "ServiceRuntime: GetPnaclFd failed -- pnacl not " | 356 "ServiceRuntime: GetPnaclFd failed -- pnacl not " |
| 353 "enabled with --enable-pnacl."); | 357 "enabled with --enable-pnacl."); |
| 354 NaClXCondVarBroadcast(&cv_); | 358 NaClXCondVarBroadcast(&cv_); |
| 355 return; | 359 return; |
| 356 } | 360 } |
| 357 } | 361 } |
| 358 // p is deleted automatically | 362 // p is deleted automatically |
| 359 } | 363 } |
| 360 | 364 |
| 361 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( | 365 void PluginReverseInterface::StreamAsFile_MainThreadContinuation( |
| 362 OpenManifestEntryResource* p, | 366 OpenManifestEntryResource* p, |
| 363 int32_t result) { | 367 int32_t result) { |
| 364 NaClLog(4, | 368 NaClLog(4, |
| 365 "Entered StreamAsFile_MainThreadContinuation\n"); | 369 "Entered StreamAsFile_MainThreadContinuation\n"); |
| 366 | 370 |
| 367 nacl::MutexLocker take(&mu_); | 371 nacl::MutexLocker take(&mu_); |
| 368 if (result == PP_OK) { | 372 if (result == PP_OK) { |
| 369 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetPOSIXFileDesc(%s)\n", | 373 NaClLog(4, "StreamAsFile_MainThreadContinuation: GetFileInfo(%s)\n", |
| 370 p->url.c_str()); | 374 p->url.c_str()); |
| 371 *p->out_desc = plugin_->GetPOSIXFileDesc(p->url); | 375 *p->file_info = plugin_->GetFileInfo(p->url); |
| 376 | |
| 372 NaClLog(4, | 377 NaClLog(4, |
| 373 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", | 378 "StreamAsFile_MainThreadContinuation: PP_OK, desc %d\n", |
| 374 *p->out_desc); | 379 p->file_info->desc); |
| 375 } else { | 380 } else { |
| 376 NaClLog(4, | 381 NaClLog(4, |
| 377 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); | 382 "StreamAsFile_MainThreadContinuation: !PP_OK, setting desc -1\n"); |
| 378 *p->out_desc = -1; | 383 p->file_info->desc = -1; |
| 379 p->error_info->SetReport(ERROR_MANIFEST_OPEN, | 384 p->error_info->SetReport(ERROR_MANIFEST_OPEN, |
| 380 "Plugin StreamAsFile failed at callback"); | 385 "Plugin StreamAsFile failed at callback"); |
| 381 } | 386 } |
| 382 *p->op_complete_ptr = true; | 387 *p->op_complete_ptr = true; |
| 383 NaClXCondVarBroadcast(&cv_); | 388 NaClXCondVarBroadcast(&cv_); |
| 384 } | 389 } |
| 385 | 390 |
| 386 | 391 |
| 387 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation( | 392 void PluginReverseInterface::BitcodeTranslate_MainThreadContinuation( |
| 388 OpenManifestEntryResource* p, | 393 OpenManifestEntryResource* p, |
| 389 int32_t result) { | 394 int32_t result) { |
| 390 NaClLog(4, | 395 NaClLog(4, |
| 391 "Entered BitcodeTranslate_MainThreadContinuation\n"); | 396 "Entered BitcodeTranslate_MainThreadContinuation\n"); |
| 392 | 397 |
| 393 nacl::MutexLocker take(&mu_); | 398 nacl::MutexLocker take(&mu_); |
| 394 if (result == PP_OK) { | 399 if (result == PP_OK) { |
| 395 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is | 400 // TODO(jvoung): clean this up. We are assuming that the NaClDesc is |
| 396 // a host IO desc and doing a downcast. Once the ReverseInterface | 401 // a host IO desc and doing a downcast. Once the ReverseInterface |
| 397 // accepts NaClDescs we can avoid this downcast. | 402 // accepts NaClDescs we can avoid this downcast. |
| 398 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc(); | 403 NaClDesc* desc = pnacl_coordinator_->ReleaseTranslatedFD()->desc(); |
| 399 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc; | 404 struct NaClDescIoDesc* ndiodp = (struct NaClDescIoDesc*)desc; |
| 400 *p->out_desc = ndiodp->hd->d; | 405 p->file_info->desc = ndiodp->hd->d; |
| 401 pnacl_coordinator_.reset(NULL); | 406 pnacl_coordinator_.reset(NULL); |
| 402 NaClLog(4, | 407 NaClLog(4, |
| 403 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n", | 408 "BitcodeTranslate_MainThreadContinuation: PP_OK, desc %d\n", |
| 404 *p->out_desc); | 409 p->file_info->desc); |
| 405 } else { | 410 } else { |
| 406 NaClLog(4, | 411 NaClLog(4, |
| 407 "BitcodeTranslate_MainThreadContinuation: !PP_OK, " | 412 "BitcodeTranslate_MainThreadContinuation: !PP_OK, " |
| 408 "setting desc -1\n"); | 413 "setting desc -1\n"); |
| 409 *p->out_desc = -1; | 414 p->file_info->desc = -1; |
| 410 // Error should have been reported by pnacl coordinator. | 415 // Error should have been reported by pnacl coordinator. |
| 411 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n"); | 416 NaClLog(LOG_ERROR, "PluginReverseInterface::BitcodeTranslate error.\n"); |
| 412 } | 417 } |
| 413 *p->op_complete_ptr = true; | 418 *p->op_complete_ptr = true; |
| 414 NaClXCondVarBroadcast(&cv_); | 419 NaClXCondVarBroadcast(&cv_); |
| 415 } | 420 } |
| 416 | 421 |
| 417 | 422 |
| 418 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { | 423 bool PluginReverseInterface::CloseManifestEntry(int32_t desc) { |
| 419 bool op_complete = false; | 424 bool op_complete = false; |
| (...skipping 426 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 846 | 851 |
| 847 nacl::string ServiceRuntime::GetCrashLogOutput() { | 852 nacl::string ServiceRuntime::GetCrashLogOutput() { |
| 848 if (NULL != subprocess_.get()) { | 853 if (NULL != subprocess_.get()) { |
| 849 return subprocess_->GetCrashLogOutput(); | 854 return subprocess_->GetCrashLogOutput(); |
| 850 } else { | 855 } else { |
| 851 return std::string(); | 856 return std::string(); |
| 852 } | 857 } |
| 853 } | 858 } |
| 854 | 859 |
| 855 } // namespace plugin | 860 } // namespace plugin |
| OLD | NEW |