Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / ppapi / native_client / src / trusted / plugin / pnacl_coordinator.cc
blob20e0a7ae34ff3f6fa0f3c04733cb2bf85af29848
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 "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
7 #include <algorithm>
8 #include <utility>
10 #include "native_client/src/include/portability_io.h"
11 #include "native_client/src/shared/platform/nacl_check.h"
12 #include "native_client/src/trusted/service_runtime/include/sys/stat.h"
14 #include "ppapi/c/pp_bool.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/c/private/ppb_uma_private.h"
18 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
19 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
20 #include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
21 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
22 #include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
24 namespace plugin {
26 namespace {
28 const int32_t kSizeKBMin = 1;
29 const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe.
30 const uint32_t kSizeKBBuckets = 100;
32 const int32_t kRatioMin = 10;
33 const int32_t kRatioMax = 10*100; // max of 10x difference.
34 const uint32_t kRatioBuckets = 100;
36 void HistogramSizeKB(pp::UMAPrivate& uma,
37 const std::string& name, int32_t kb) {
38 if (kb < 0) return;
39 uma.HistogramCustomCounts(name,
40 kb,
41 kSizeKBMin, kSizeKBMax,
42 kSizeKBBuckets);
45 void HistogramRatio(pp::UMAPrivate& uma,
46 const std::string& name, int64_t a, int64_t b) {
47 if (a < 0 || b <= 0) return;
48 uma.HistogramCustomCounts(name,
49 static_cast<int32_t>(100 * a / b),
50 kRatioMin, kRatioMax,
51 kRatioBuckets);
54 std::string GetArchitectureAttributes(Plugin* plugin) {
55 pp::Var attrs_var(pp::PASS_REF,
56 plugin->nacl_interface()->GetCpuFeatureAttrs());
57 return attrs_var.AsString();
60 void DidCacheHit(void* user_data, PP_FileHandle nexe_file_handle) {
61 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
62 coordinator->BitcodeStreamCacheHit(nexe_file_handle);
65 void DidCacheMiss(void* user_data, int64_t expected_pexe_size,
66 PP_FileHandle temp_nexe_file) {
67 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
68 coordinator->BitcodeStreamCacheMiss(expected_pexe_size,
69 temp_nexe_file);
72 void DidStreamData(void* user_data, const void* stream_data, int32_t length) {
73 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
74 coordinator->BitcodeStreamGotData(stream_data, length);
77 void DidFinishStream(void* user_data, int32_t pp_error) {
78 PnaclCoordinator* coordinator = static_cast<PnaclCoordinator*>(user_data);
79 coordinator->BitcodeStreamDidFinish(pp_error);
82 PPP_PexeStreamHandler kPexeStreamHandler = {
83 &DidCacheHit,
84 &DidCacheMiss,
85 &DidStreamData,
86 &DidFinishStream
89 } // namespace
91 PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
92 Plugin* plugin,
93 const std::string& pexe_url,
94 const PP_PNaClOptions& pnacl_options,
95 const pp::CompletionCallback& translate_notify_callback) {
96 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
97 static_cast<void*>(plugin), pexe_url.c_str()));
98 PnaclCoordinator* coordinator =
99 new PnaclCoordinator(plugin, pexe_url,
100 pnacl_options,
101 translate_notify_callback);
103 GetNaClInterface()->SetPNaClStartTime(plugin->pp_instance());
104 int cpus = plugin->nacl_interface()->GetNumberOfProcessors();
105 coordinator->split_module_count_ = std::min(4, std::max(1, cpus));
107 // First start a network request for the pexe, to tickle the component
108 // updater's On-Demand resource throttler, and to get Last-Modified/ETag
109 // cache information. We can cancel the request later if there's
110 // a bitcode->nexe cache hit.
111 coordinator->OpenBitcodeStream();
112 return coordinator;
115 PnaclCoordinator::PnaclCoordinator(
116 Plugin* plugin,
117 const std::string& pexe_url,
118 const PP_PNaClOptions& pnacl_options,
119 const pp::CompletionCallback& translate_notify_callback)
120 : translate_finish_error_(PP_OK),
121 plugin_(plugin),
122 translate_notify_callback_(translate_notify_callback),
123 translation_finished_reported_(false),
124 pexe_url_(pexe_url),
125 pnacl_options_(pnacl_options),
126 architecture_attributes_(GetArchitectureAttributes(plugin)),
127 split_module_count_(1),
128 error_already_reported_(false),
129 pexe_size_(0),
130 pexe_bytes_compiled_(0),
131 expected_pexe_size_(-1) {
132 callback_factory_.Initialize(this);
135 PnaclCoordinator::~PnaclCoordinator() {
136 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
137 "translate_thread=%p\n",
138 static_cast<void*>(this), translate_thread_.get()));
139 // Stopping the translate thread will cause the translate thread to try to
140 // run translation_complete_callback_ on the main thread. This destructor is
141 // running from the main thread, and by the time it exits, callback_factory_
142 // will have been destroyed. This will result in the cancellation of
143 // translation_complete_callback_, so no notification will be delivered.
144 if (translate_thread_.get() != NULL)
145 translate_thread_->AbortSubprocesses();
146 if (!translation_finished_reported_) {
147 plugin_->nacl_interface()->ReportTranslationFinished(
148 plugin_->pp_instance(),
149 PP_FALSE, 0, 0, 0);
151 // Force deleting the translate_thread now. It must be deleted
152 // before any scoped_* fields hanging off of PnaclCoordinator
153 // since the thread may be accessing those fields.
154 // It will also be accessing obj_files_.
155 translate_thread_.reset(NULL);
156 for (size_t i = 0; i < obj_files_.size(); i++)
157 delete obj_files_[i];
160 PP_FileHandle PnaclCoordinator::TakeTranslatedFileHandle() {
161 DCHECK(temp_nexe_file_ != NULL);
162 return temp_nexe_file_->TakeFileHandle();
165 void PnaclCoordinator::ReportNonPpapiError(PP_NaClError err_code,
166 const std::string& message) {
167 ErrorInfo error_info;
168 error_info.SetReport(err_code, message);
169 plugin_->ReportLoadError(error_info);
170 ExitWithError();
173 void PnaclCoordinator::ReportPpapiError(PP_NaClError err_code,
174 int32_t pp_error,
175 const std::string& message) {
176 std::stringstream ss;
177 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
178 ErrorInfo error_info;
179 error_info.SetReport(err_code, ss.str());
180 plugin_->ReportLoadError(error_info);
181 ExitWithError();
184 void PnaclCoordinator::ExitWithError() {
185 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError\n"));
186 // Free all the intermediate callbacks we ever created.
187 // Note: this doesn't *cancel* the callbacks from the factories attached
188 // to the various helper classes (e.g., pnacl_resources). Thus, those
189 // callbacks may still run asynchronously. We let those run but ignore
190 // any other errors they may generate so that they do not end up running
191 // translate_notify_callback_, which has already been freed.
192 callback_factory_.CancelAll();
193 if (!error_already_reported_) {
194 error_already_reported_ = true;
195 translation_finished_reported_ = true;
196 plugin_->nacl_interface()->ReportTranslationFinished(
197 plugin_->pp_instance(),
198 PP_FALSE, 0, 0, 0);
199 translate_notify_callback_.Run(PP_ERROR_FAILED);
203 // Signal that Pnacl translation completed normally.
204 void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
205 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
206 NACL_PRId32 ")\n", pp_error));
207 // Bail out if there was an earlier error (e.g., pexe load failure),
208 // or if there is an error from the translation thread.
209 if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
210 plugin_->ReportLoadError(error_info_);
211 ExitWithError();
212 return;
215 // Send out one last progress event, to finish up the progress events
216 // that were delayed (see the delay inserted in BitcodeGotCompiled).
217 if (expected_pexe_size_ != -1) {
218 pexe_bytes_compiled_ = expected_pexe_size_;
219 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
220 PP_NACL_EVENT_PROGRESS,
221 pexe_url_.c_str(),
222 PP_TRUE,
223 pexe_bytes_compiled_,
224 expected_pexe_size_);
226 struct nacl_abi_stat stbuf;
227 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
228 if (0 == (*((struct NaClDescVtbl const *)desc->base.vtbl)->Fstat)(desc,
229 &stbuf)) {
230 nacl_abi_off_t nexe_size = stbuf.nacl_abi_st_size;
231 HistogramSizeKB(plugin_->uma_interface(),
232 "NaCl.Perf.Size.PNaClTranslatedNexe",
233 static_cast<int32_t>(nexe_size / 1024));
234 HistogramRatio(plugin_->uma_interface(),
235 "NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
237 // The nexe is written to the temp_nexe_file_. We must Reset() the file
238 // pointer to be able to read it again from the beginning.
239 temp_nexe_file_->Reset();
241 // Report to the browser that translation finished. The browser will take
242 // care of storing the nexe in the cache.
243 translation_finished_reported_ = true;
244 plugin_->nacl_interface()->ReportTranslationFinished(
245 plugin_->pp_instance(), PP_TRUE, pnacl_options_.opt_level,
246 pexe_size_, translate_thread_->GetCompileTime());
248 NexeReadDidOpen(PP_OK);
251 void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
252 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
253 NACL_PRId32 ")\n", pp_error));
254 if (pp_error != PP_OK) {
255 if (pp_error == PP_ERROR_FILENOTFOUND) {
256 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOTFOUND,
257 pp_error,
258 "Failed to open translated nexe (not found).");
259 return;
261 if (pp_error == PP_ERROR_NOACCESS) {
262 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_NOACCESS,
263 pp_error,
264 "Failed to open translated nexe (no access).");
265 return;
267 ReportPpapiError(PP_NACL_ERROR_PNACL_CACHE_FETCH_OTHER,
268 pp_error,
269 "Failed to open translated nexe.");
270 return;
273 translate_notify_callback_.Run(PP_OK);
276 void PnaclCoordinator::OpenBitcodeStream() {
277 // Even though we haven't started downloading, create the translation
278 // thread object immediately. This ensures that any pieces of the file
279 // that get downloaded before the compilation thread is accepting
280 // SRPCs won't get dropped.
281 translate_thread_.reset(new PnaclTranslateThread());
282 if (translate_thread_ == NULL) {
283 ReportNonPpapiError(
284 PP_NACL_ERROR_PNACL_THREAD_CREATE,
285 "PnaclCoordinator: could not allocate translation thread.");
286 return;
289 GetNaClInterface()->StreamPexe(plugin_->pp_instance(),
290 pexe_url_.c_str(),
291 pnacl_options_.opt_level,
292 &kPexeStreamHandler,
293 this);
296 void PnaclCoordinator::BitcodeStreamCacheHit(PP_FileHandle handle) {
297 if (handle == PP_kInvalidFileHandle) {
298 ReportNonPpapiError(
299 PP_NACL_ERROR_PNACL_CREATE_TEMP,
300 std::string(
301 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
302 BitcodeStreamDidFinish(PP_ERROR_FAILED);
303 return;
305 temp_nexe_file_.reset(new TempFile(plugin_, handle));
306 // Open it for reading as the cached nexe file.
307 NexeReadDidOpen(temp_nexe_file_->Open(false));
310 void PnaclCoordinator::BitcodeStreamCacheMiss(int64_t expected_pexe_size,
311 PP_FileHandle nexe_handle) {
312 // IMPORTANT: Make sure that PnaclResources::StartLoad() is only
313 // called after you receive a response to a request for a .pexe file.
315 // The component updater's resource throttles + OnDemand update/install
316 // should block the URL request until the compiler is present. Now we
317 // can load the resources (e.g. llc and ld nexes).
318 resources_.reset(new PnaclResources(plugin_));
319 CHECK(resources_ != NULL);
321 // The first step of loading resources: read the resource info file.
322 if (!resources_->ReadResourceInfo()) {
323 ExitWithError();
324 return;
327 // Second step of loading resources: call StartLoad to load pnacl-llc
328 // and pnacl-ld, based on the filenames found in the resource info file.
329 if (!resources_->StartLoad()) {
330 ReportNonPpapiError(
331 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
332 std::string("The Portable Native Client (pnacl) component is not "
333 "installed. Please consult chrome://components for more "
334 "information."));
335 return;
338 expected_pexe_size_ = expected_pexe_size;
340 for (int i = 0; i < split_module_count_; i++) {
341 PP_FileHandle obj_handle =
342 plugin_->nacl_interface()->CreateTemporaryFile(plugin_->pp_instance());
343 nacl::scoped_ptr<TempFile> temp_file(new TempFile(plugin_, obj_handle));
344 int32_t pp_error = temp_file->Open(true);
345 if (pp_error != PP_OK) {
346 ReportPpapiError(PP_NACL_ERROR_PNACL_CREATE_TEMP,
347 pp_error,
348 "Failed to open scratch object file.");
349 return;
350 } else {
351 obj_files_.push_back(temp_file.release());
354 invalid_desc_wrapper_.reset(plugin_->wrapper_factory()->MakeInvalid());
356 temp_nexe_file_.reset(new TempFile(plugin_, nexe_handle));
357 // Open the nexe file for connecting ld and sel_ldr.
358 // Start translation when done with this last step of setup!
359 RunTranslate(temp_nexe_file_->Open(true));
362 void PnaclCoordinator::BitcodeStreamGotData(const void* data, int32_t length) {
363 DCHECK(translate_thread_.get());
365 translate_thread_->PutBytes(data, length);
366 if (data && length > 0)
367 pexe_size_ += length;
370 void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
371 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
372 NACL_PRId32 ")\n", pp_error));
373 if (pp_error != PP_OK) {
374 // Defer reporting the error and cleanup until after the translation
375 // thread returns, because it may be accessing the coordinator's
376 // objects or writing to the files.
377 translate_finish_error_ = pp_error;
378 if (pp_error == PP_ERROR_ABORTED) {
379 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_ABORTED,
380 "PnaclCoordinator: pexe load failed (aborted).");
382 if (pp_error == PP_ERROR_NOACCESS) {
383 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_NOACCESS,
384 "PnaclCoordinator: pexe load failed (no access).");
385 } else {
386 std::stringstream ss;
387 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
388 error_info_.SetReport(PP_NACL_ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
391 if (translate_thread_->started())
392 translate_thread_->AbortSubprocesses();
393 else
394 TranslateFinished(pp_error);
395 } else {
396 // Compare download completion pct (100% now), to compile completion pct.
397 HistogramRatio(plugin_->uma_interface(),
398 "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
399 pexe_bytes_compiled_, pexe_size_);
400 translate_thread_->EndStream();
404 void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
405 int64_t bytes_compiled) {
406 DCHECK(pp_error == PP_OK);
407 pexe_bytes_compiled_ += bytes_compiled;
408 // Hold off reporting the last few bytes of progress, since we don't know
409 // when they are actually completely compiled. "bytes_compiled" only means
410 // that bytes were sent to the compiler.
411 if (expected_pexe_size_ != -1) {
412 if (!ShouldDelayProgressEvent()) {
413 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
414 PP_NACL_EVENT_PROGRESS,
415 pexe_url_.c_str(),
416 PP_TRUE,
417 pexe_bytes_compiled_,
418 expected_pexe_size_);
420 } else {
421 GetNaClInterface()->DispatchEvent(plugin_->pp_instance(),
422 PP_NACL_EVENT_PROGRESS,
423 pexe_url_.c_str(),
424 PP_FALSE,
425 pexe_bytes_compiled_,
426 expected_pexe_size_);
430 pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
431 int64_t bytes_compiled) {
432 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
433 bytes_compiled);
436 void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
437 int64_t* bytes_total) {
438 *bytes_loaded = pexe_bytes_compiled_;
439 *bytes_total = expected_pexe_size_;
442 void PnaclCoordinator::RunTranslate(int32_t pp_error) {
443 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
444 NACL_PRId32 ")\n", pp_error));
445 // Invoke llc followed by ld off the main thread. This allows use of
446 // blocking RPCs that would otherwise block the JavaScript main thread.
447 pp::CompletionCallback report_translate_finished =
448 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
450 CHECK(translate_thread_ != NULL);
451 translate_thread_->RunTranslate(report_translate_finished,
452 &obj_files_,
453 temp_nexe_file_.get(),
454 invalid_desc_wrapper_.get(),
455 &error_info_,
456 resources_.get(),
457 &pnacl_options_,
458 architecture_attributes_,
459 this,
460 plugin_);
463 } // namespace plugin