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