blob: 18b4faec4c2d4e9da747f2eef4f9e72b74c9b63e [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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
Ben Murdochca12bfa2013-07-23 11:17:05 +01005#include "ppapi/native_client/src/trusted/plugin/pnacl_coordinator.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00006
7#include <utility>
8#include <vector>
9
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"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000013#include "native_client/src/trusted/service_runtime/include/sys/stat.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014
15#include "ppapi/c/pp_bool.h"
16#include "ppapi/c/pp_errors.h"
17#include "ppapi/c/ppb_file_io.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000018#include "ppapi/c/private/ppb_uma_private.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000019#include "ppapi/cpp/file_io.h"
20
Ben Murdochca12bfa2013-07-23 11:17:05 +010021#include "ppapi/native_client/src/trusted/plugin/local_temp_file.h"
22#include "ppapi/native_client/src/trusted/plugin/manifest.h"
23#include "ppapi/native_client/src/trusted/plugin/nacl_http_response_headers.h"
24#include "ppapi/native_client/src/trusted/plugin/plugin.h"
25#include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
26#include "ppapi/native_client/src/trusted/plugin/pnacl_translate_thread.h"
27#include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
28#include "ppapi/native_client/src/trusted/plugin/temporary_file.h"
29
Torne (Richard Coles)58218062012-11-14 11:43:16 +000030namespace {
31const char kPnaclTempDir[] = "/.pnacl";
32const uint32_t kCopyBufSize = 512 << 10;
33}
34
35namespace plugin {
36
37//////////////////////////////////////////////////////////////////////
38// Pnacl-specific manifest support.
39//////////////////////////////////////////////////////////////////////
40
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010041// The PNaCl linker gets file descriptors via the service runtime's
42// reverse service lookup. The reverse service lookup requires a manifest.
43// Normally, that manifest is an NMF containing mappings for shared libraries.
44// Here, we provide a manifest that redirects to PNaCl component files
45// that are part of Chrome.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000046class PnaclManifest : public Manifest {
47 public:
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010048 PnaclManifest() : manifest_base_url_(PnaclUrls::GetBaseUrl()) { }
Torne (Richard Coles)58218062012-11-14 11:43:16 +000049 virtual ~PnaclManifest() { }
50
51 virtual bool GetProgramURL(nacl::string* full_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000052 PnaclOptions* pnacl_options,
53 ErrorInfo* error_info) const {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000054 // Does not contain program urls.
55 UNREFERENCED_PARAMETER(full_url);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000056 UNREFERENCED_PARAMETER(pnacl_options);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000057 UNREFERENCED_PARAMETER(error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000058 PLUGIN_PRINTF(("PnaclManifest does not contain a program\n"));
59 error_info->SetReport(ERROR_MANIFEST_GET_NEXE_URL,
60 "pnacl manifest does not contain a program.");
61 return false;
62 }
63
64 virtual bool ResolveURL(const nacl::string& relative_url,
65 nacl::string* full_url,
66 ErrorInfo* error_info) const {
67 // Does not do general URL resolution, simply appends relative_url to
68 // the end of manifest_base_url_.
69 UNREFERENCED_PARAMETER(error_info);
70 *full_url = manifest_base_url_ + relative_url;
71 return true;
72 }
73
74 virtual bool GetFileKeys(std::set<nacl::string>* keys) const {
75 // Does not support enumeration.
76 PLUGIN_PRINTF(("PnaclManifest does not support key enumeration\n"));
77 UNREFERENCED_PARAMETER(keys);
78 return false;
79 }
80
81 virtual bool ResolveKey(const nacl::string& key,
82 nacl::string* full_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000083 PnaclOptions* pnacl_options,
84 ErrorInfo* error_info) const {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010085 // All of the component files are native (do not require pnacl translate).
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000086 pnacl_options->set_translate(false);
Torne (Richard Coles)58218062012-11-14 11:43:16 +000087 // We can only resolve keys in the files/ namespace.
88 const nacl::string kFilesPrefix = "files/";
89 size_t files_prefix_pos = key.find(kFilesPrefix);
90 if (files_prefix_pos == nacl::string::npos) {
91 error_info->SetReport(ERROR_MANIFEST_RESOLVE_URL,
92 "key did not start with files/");
93 return false;
94 }
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010095 // Resolve the full URL to the file. Provide it with a platform-specific
96 // prefix.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000097 nacl::string key_basename = key.substr(kFilesPrefix.length());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010098 return ResolveURL(PnaclUrls::PrependPlatformPrefix(key_basename),
99 full_url, error_info);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000100 }
101
102 private:
103 NACL_DISALLOW_COPY_AND_ASSIGN(PnaclManifest);
104
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000105 nacl::string manifest_base_url_;
106};
107
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000108//////////////////////////////////////////////////////////////////////
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000109// UMA stat helpers.
110//////////////////////////////////////////////////////////////////////
111
112namespace {
113
114// Assume translation time metrics *can be* large.
115// Up to 12 minutes.
116const int64_t kTimeLargeMin = 10; // in ms
117const int64_t kTimeLargeMax = 720000; // in ms
118const uint32_t kTimeLargeBuckets = 100;
119
120const int32_t kSizeKBMin = 1;
121const int32_t kSizeKBMax = 512*1024; // very large .pexe / .nexe.
122const uint32_t kSizeKBBuckets = 100;
123
124const int32_t kRatioMin = 10;
125const int32_t kRatioMax = 10*100; // max of 10x difference.
126const uint32_t kRatioBuckets = 100;
127
128const int32_t kKBPSMin = 1;
129const int32_t kKBPSMax = 30*1000; // max of 30 MB / sec.
130const uint32_t kKBPSBuckets = 100;
131
132const PPB_UMA_Private* uma_interface = NULL;
133
134const PPB_UMA_Private* GetUMAInterface() {
135 if (uma_interface != NULL) {
136 return uma_interface;
137 }
138 pp::Module *module = pp::Module::Get();
139 DCHECK(module);
140 uma_interface = static_cast<const PPB_UMA_Private*>(
141 module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE));
142 return uma_interface;
143}
144
145void HistogramTime(const std::string& name, int64_t ms) {
146 if (ms < 0) return;
147
148 const PPB_UMA_Private* ptr = GetUMAInterface();
149 if (ptr == NULL) return;
150
151 ptr->HistogramCustomTimes(pp::Var(name).pp_var(),
152 ms,
153 kTimeLargeMin, kTimeLargeMax,
154 kTimeLargeBuckets);
155}
156
157void HistogramSizeKB(const std::string& name, int32_t kb) {
158 if (kb < 0) return;
159
160 const PPB_UMA_Private* ptr = GetUMAInterface();
161 if (ptr == NULL) return;
162
163 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
164 kb,
165 kSizeKBMin, kSizeKBMax,
166 kSizeKBBuckets);
167}
168
169void HistogramRatio(const std::string& name, int64_t a, int64_t b) {
170 if (a < 0 || b <= 0) return;
171
172 const PPB_UMA_Private* ptr = GetUMAInterface();
173 if (ptr == NULL) return;
174
175 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
176 100 * a / b,
177 kRatioMin, kRatioMax,
178 kRatioBuckets);
179}
180
181void HistogramKBPerSec(const std::string& name, double kb, double s) {
182 if (kb < 0.0 || s <= 0.0) return;
183
184 const PPB_UMA_Private* ptr = GetUMAInterface();
185 if (ptr == NULL) return;
186
187 ptr->HistogramCustomCounts(pp::Var(name).pp_var(),
188 static_cast<int64_t>(kb / s),
189 kKBPSMin, kKBPSMax,
190 kKBPSBuckets);
191}
192
193void HistogramEnumerateTranslationCache(bool hit) {
194 const PPB_UMA_Private* ptr = GetUMAInterface();
195 if (ptr == NULL) return;
196 ptr->HistogramEnumeration(pp::Var("NaCl.Perf.PNaClCache.IsHit").pp_var(),
197 hit, 2);
198}
199
200// Opt level is expected to be 0 to 3. Treating 4 as unknown.
201const int8_t kOptUnknown = 4;
202
203void HistogramOptLevel(int8_t opt_level) {
204 const PPB_UMA_Private* ptr = GetUMAInterface();
205 if (ptr == NULL) return;
206 if (opt_level < 0 || opt_level > 3) {
207 opt_level = kOptUnknown;
208 }
209 ptr->HistogramEnumeration(pp::Var("NaCl.Options.PNaCl.OptLevel").pp_var(),
210 opt_level, kOptUnknown+1);
211}
212
213} // namespace
214
215
216//////////////////////////////////////////////////////////////////////
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000217// The coordinator class.
218//////////////////////////////////////////////////////////////////////
219
220// Out-of-line destructor to keep it from getting put in every .o where
221// callback_source.h is included
222template<>
223CallbackSource<FileStreamData>::~CallbackSource() {}
224
225PnaclCoordinator* PnaclCoordinator::BitcodeToNative(
226 Plugin* plugin,
227 const nacl::string& pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000228 const PnaclOptions& pnacl_options,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000229 const pp::CompletionCallback& translate_notify_callback) {
230 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (plugin=%p, pexe=%s)\n",
231 static_cast<void*>(plugin), pexe_url.c_str()));
232 PnaclCoordinator* coordinator =
233 new PnaclCoordinator(plugin, pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234 pnacl_options,
235 translate_notify_callback);
236 coordinator->pnacl_init_time_ = NaClGetTimeOfDayMicroseconds();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000237 coordinator->off_the_record_ =
238 plugin->nacl_interface()->IsOffTheRecord();
239 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeToNative (manifest=%p, "
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000240 "off_the_record=%d)\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000241 reinterpret_cast<const void*>(coordinator->manifest_.get()),
242 coordinator->off_the_record_));
243
Ben Murdoch2385ea32013-08-06 11:01:04 +0100244 // First check that PNaCl is installed.
245 pp::CompletionCallback pnacl_installed_cb =
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100246 coordinator->callback_factory_.NewCallback(
Ben Murdoch2385ea32013-08-06 11:01:04 +0100247 &PnaclCoordinator::DidCheckPnaclInstalled);
248 plugin->nacl_interface()->EnsurePnaclInstalled(
249 plugin->pp_instance(),
250 pnacl_installed_cb.pp_completion_callback());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100251 return coordinator;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000252}
253
254PnaclCoordinator::PnaclCoordinator(
255 Plugin* plugin,
256 const nacl::string& pexe_url,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000257 const PnaclOptions& pnacl_options,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000258 const pp::CompletionCallback& translate_notify_callback)
259 : translate_finish_error_(PP_OK),
260 plugin_(plugin),
261 translate_notify_callback_(translate_notify_callback),
262 file_system_(new pp::FileSystem(plugin, PP_FILESYSTEMTYPE_LOCALTEMPORARY)),
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100263 manifest_(new PnaclManifest()),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000264 pexe_url_(pexe_url),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000265 pnacl_options_(pnacl_options),
Ben Murdocheb525c52013-07-10 11:40:50 +0100266 use_new_cache_(false),
267 is_cache_hit_(PP_FALSE),
268 nexe_handle_(PP_kInvalidFileHandle),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000269 error_already_reported_(false),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000270 off_the_record_(false),
271 pnacl_init_time_(0),
272 pexe_size_(0),
273 pexe_bytes_compiled_(0),
274 expected_pexe_size_(-1) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000275 PLUGIN_PRINTF(("PnaclCoordinator::PnaclCoordinator (this=%p, plugin=%p)\n",
276 static_cast<void*>(this), static_cast<void*>(plugin)));
277 callback_factory_.Initialize(this);
Ben Murdocheb525c52013-07-10 11:40:50 +0100278 if (getenv("PNACL_USE_NEW_CACHE")) {
279 PLUGIN_PRINTF(("PnaclCoordinator using new translation cache\n"));
280 use_new_cache_ = true;
281 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000282}
283
284PnaclCoordinator::~PnaclCoordinator() {
285 PLUGIN_PRINTF(("PnaclCoordinator::~PnaclCoordinator (this=%p, "
286 "translate_thread=%p\n",
287 static_cast<void*>(this), translate_thread_.get()));
288 // Stopping the translate thread will cause the translate thread to try to
289 // run translation_complete_callback_ on the main thread. This destructor is
290 // running from the main thread, and by the time it exits, callback_factory_
291 // will have been destroyed. This will result in the cancellation of
292 // translation_complete_callback_, so no notification will be delivered.
293 if (translate_thread_.get() != NULL) {
294 translate_thread_->AbortSubprocesses();
295 }
296}
297
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000298void PnaclCoordinator::ReportNonPpapiError(enum PluginErrorCode err_code,
299 const nacl::string& message) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100300 error_info_.SetReport(err_code, message);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000301 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000302}
303
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000304void PnaclCoordinator::ReportPpapiError(enum PluginErrorCode err_code,
305 int32_t pp_error,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000306 const nacl::string& message) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000307 nacl::stringstream ss;
308 ss << "PnaclCoordinator: " << message << " (pp_error=" << pp_error << ").";
309 error_info_.SetReport(err_code, ss.str());
310 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000311}
312
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000313void PnaclCoordinator::ExitWithError() {
314 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError (error_code=%d, "
315 "message='%s')\n",
316 error_info_.error_code(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000317 error_info_.message().c_str()));
318 plugin_->ReportLoadError(error_info_);
319 // Free all the intermediate callbacks we ever created.
320 // Note: this doesn't *cancel* the callbacks from the factories attached
321 // to the various helper classes (e.g., pnacl_resources). Thus, those
322 // callbacks may still run asynchronously. We let those run but ignore
323 // any other errors they may generate so that they do not end up running
324 // translate_notify_callback_, which has already been freed.
325 callback_factory_.CancelAll();
326 if (!error_already_reported_) {
327 error_already_reported_ = true;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000328 translate_notify_callback_.Run(PP_ERROR_FAILED);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000329 } else {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000330 PLUGIN_PRINTF(("PnaclCoordinator::ExitWithError an earlier error was "
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000331 "already reported -- Skipping.\n"));
332 }
333}
334
335// Signal that Pnacl translation completed normally.
336void PnaclCoordinator::TranslateFinished(int32_t pp_error) {
337 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100338 NACL_PRId32 ")\n", pp_error));
Ben Murdochbb1529c2013-08-08 10:24:53 +0100339 // Bail out if there was an earlier error (e.g., pexe load failure),
340 // or if there is an error from the translation thread.
341 if (translate_finish_error_ != PP_OK || pp_error != PP_OK) {
342 if (use_new_cache_) {
343 plugin_->nacl_interface()->ReportTranslationFinished(
344 plugin_->pp_instance(),
345 PP_FALSE);
346 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000347 ExitWithError();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000348 return;
349 }
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100350 // Send out one last progress event, to finish up the progress events
351 // that were delayed (see the delay inserted in BitcodeGotCompiled).
352 if (ExpectedProgressKnown()) {
353 pexe_bytes_compiled_ = expected_pexe_size_;
354 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
355 pexe_url_,
356 plugin::Plugin::LENGTH_IS_COMPUTABLE,
357 pexe_bytes_compiled_,
358 expected_pexe_size_);
359 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000360
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000361 // If there are no errors, report stats from this thread (the main thread).
362 HistogramOptLevel(pnacl_options_.opt_level());
363 const plugin::PnaclTimeStats& time_stats = translate_thread_->GetTimeStats();
364 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadCompiler",
365 time_stats.pnacl_llc_load_time / NACL_MICROS_PER_MILLI);
366 HistogramTime("NaCl.Perf.PNaClLoadTime.CompileTime",
367 time_stats.pnacl_compile_time / NACL_MICROS_PER_MILLI);
368 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
369 pexe_size_ / 1024.0,
370 time_stats.pnacl_compile_time / 1000000.0);
371 HistogramTime("NaCl.Perf.PNaClLoadTime.LoadLinker",
372 time_stats.pnacl_ld_load_time / NACL_MICROS_PER_MILLI);
373 HistogramTime("NaCl.Perf.PNaClLoadTime.LinkTime",
374 time_stats.pnacl_link_time / NACL_MICROS_PER_MILLI);
375 HistogramSizeKB("NaCl.Perf.Size.Pexe",
376 static_cast<int64_t>(pexe_size_ / 1024));
377
378 struct nacl_abi_stat stbuf;
379 struct NaClDesc* desc = temp_nexe_file_->read_wrapper()->desc();
380 int stat_ret;
381 if (0 != (stat_ret = (*((struct NaClDescVtbl const *) desc->base.vtbl)->
382 Fstat)(desc, &stbuf))) {
383 PLUGIN_PRINTF(("PnaclCoordinator::TranslateFinished can't stat nexe.\n"));
384 } else {
385 size_t nexe_size = stbuf.nacl_abi_st_size;
386 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe",
387 static_cast<int64_t>(nexe_size / 1024));
388 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct", pexe_size_, nexe_size);
389 }
390
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000391 // The nexe is written to the temp_nexe_file_. We must Reset() the file
392 // pointer to be able to read it again from the beginning.
393 temp_nexe_file_->Reset();
394
Ben Murdocheb525c52013-07-10 11:40:50 +0100395 if (use_new_cache_) {
396 // Report to the browser that translation finished. The browser will take
397 // care of caching.
398 plugin_->nacl_interface()->ReportTranslationFinished(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100399 plugin_->pp_instance(), PP_TRUE);
Ben Murdocheb525c52013-07-10 11:40:50 +0100400 NexeReadDidOpen(PP_OK);
401 return;
402 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000403 if (pnacl_options_.HasCacheKey() && cached_nexe_file_ != NULL) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000404 // We are using a cache, but had a cache miss, which is why we did the
405 // translation. Reset cached_nexe_file_ to have a random name,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000406 // for scratch purposes, before renaming to the final cache_identity.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000407 cached_nexe_file_.reset(new LocalTempFile(plugin_, file_system_.get(),
408 nacl::string(kPnaclTempDir)));
409 pp::CompletionCallback cb = callback_factory_.NewCallback(
410 &PnaclCoordinator::CachedNexeOpenedForWrite);
411 cached_nexe_file_->OpenWrite(cb);
412 } else {
413 // For now, tolerate bitcode that is missing a cache identity, and
414 // tolerate the lack of caching in incognito mode.
415 PLUGIN_PRINTF(("PnaclCoordinator -- not caching.\n"));
416 NexeReadDidOpen(PP_OK);
417 }
418}
419
420void PnaclCoordinator::CachedNexeOpenedForWrite(int32_t pp_error) {
421 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000422 if (pp_error == PP_ERROR_NOACCESS) {
423 ReportPpapiError(
424 ERROR_PNACL_CACHE_FILEOPEN_NOACCESS,
425 pp_error,
426 "PNaCl translation cache failed to open file for write "
427 "(no access).");
428 return;
429 }
430 if (pp_error == PP_ERROR_NOQUOTA) {
431 ReportPpapiError(
432 ERROR_PNACL_CACHE_FILEOPEN_NOQUOTA,
433 pp_error,
434 "PNaCl translation cache failed to open file for write "
435 "(no quota).");
436 return;
437 }
438 if (pp_error == PP_ERROR_NOSPACE) {
439 ReportPpapiError(
440 ERROR_PNACL_CACHE_FILEOPEN_NOSPACE,
441 pp_error,
442 "PNaCl translation cache failed to open file for write "
443 "(no space).");
444 return;
445 }
446 if (pp_error == PP_ERROR_NOTAFILE) {
447 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_NOTAFILE,
448 pp_error,
449 "PNaCl translation cache failed to open file for write."
450 " File already exists as a directory.");
451 return;
452 }
453 ReportPpapiError(ERROR_PNACL_CACHE_FILEOPEN_OTHER,
454 pp_error,
455 "PNaCl translation cache failed to open file for write.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000456 return;
457 }
458
459 // Copy the contents from temp_nexe_file_ -> cached_nexe_file_,
460 // then rename the cached_nexe_file_ file to the cache id.
461 int64_t cur_offset = 0;
462 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
463 char buf[kCopyBufSize];
464 int32_t num_read =
465 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
466 // Hit EOF or something.
467 if (num_read == 0) {
468 NexeWasCopiedToCache(PP_OK);
469 return;
470 }
471 if (num_read < 0) {
472 PLUGIN_PRINTF(("PnaclCoordinator::CachedNexeOpenedForWrite read failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100473 "(error=%" NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000474 NexeWasCopiedToCache(PP_ERROR_FAILED);
475 return;
476 }
477 pp::CompletionCallback cb = callback_factory_.NewCallback(
478 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, cur_offset);
479 cached_nexe_file_->write_file_io()->Write(cur_offset, buf, num_read, cb);
480}
481
482void PnaclCoordinator::DidCopyNexeToCachePartial(int32_t pp_error,
483 int32_t num_read_prev,
484 int64_t cur_offset) {
485 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100486 "(pp_error=%" NACL_PRId32 ", num_read_prev=%" NACL_PRId32
487 ", cur_offset=%" NACL_PRId64 ").\n",
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000488 pp_error, num_read_prev, cur_offset));
489 // Assume we are done.
490 if (pp_error == PP_OK) {
491 NexeWasCopiedToCache(PP_OK);
492 return;
493 }
494 if (pp_error < PP_OK) {
495 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial failed (err=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100496 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000497 NexeWasCopiedToCache(pp_error);
498 return;
499 }
500
501 // Check if we wrote as much as we read.
502 nacl::DescWrapper* read_wrapper = temp_nexe_file_->read_wrapper();
503 if (pp_error != num_read_prev) {
504 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial partial "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100505 "write (bytes_written=%" NACL_PRId32 " vs "
506 "read=%" NACL_PRId32 ")\n", pp_error, num_read_prev));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000507 CHECK(pp_error < num_read_prev);
508 // Seek back to re-read the bytes that were not written.
509 nacl_off64_t seek_result =
510 read_wrapper->Seek(pp_error - num_read_prev, SEEK_CUR);
511 if (seek_result < 0) {
512 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial seek failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100513 "(err=%" NACL_PRId64 ")\n", seek_result));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000514 NexeWasCopiedToCache(PP_ERROR_FAILED);
515 return;
516 }
517 }
518
519 int64_t next_offset = cur_offset + pp_error;
520 char buf[kCopyBufSize];
521 int32_t num_read =
522 nacl::assert_cast<int32_t>(read_wrapper->Read(buf, sizeof buf));
523 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read (bytes=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100524 NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000525 // Hit EOF or something.
526 if (num_read == 0) {
527 NexeWasCopiedToCache(PP_OK);
528 return;
529 }
530 if (num_read < 0) {
531 PLUGIN_PRINTF(("PnaclCoordinator::DidCopyNexeToCachePartial read failed "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100532 "(error=%" NACL_PRId32 ")\n", num_read));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000533 NexeWasCopiedToCache(PP_ERROR_FAILED);
534 return;
535 }
536 pp::CompletionCallback cb = callback_factory_.NewCallback(
537 &PnaclCoordinator::DidCopyNexeToCachePartial, num_read, next_offset);
538 PLUGIN_PRINTF(("PnaclCoordinator::CopyNexeToCache Writing ("
Ben Murdochbbcdd452013-07-25 10:06:34 +0100539 "bytes=%" NACL_PRId32 ", buf=%p, file_io=%p)\n", num_read, buf,
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000540 cached_nexe_file_->write_file_io()));
541 cached_nexe_file_->write_file_io()->Write(next_offset, buf, num_read, cb);
542}
543
544void PnaclCoordinator::NexeWasCopiedToCache(int32_t pp_error) {
545 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000546 // Try to delete the partially written not-yet-committed cache file before
547 // returning. We pass the current pp_error along so that it can be reported
548 // before returning.
549 pp::CompletionCallback cb = callback_factory_.NewCallback(
550 &PnaclCoordinator::CorruptCacheFileWasDeleted, pp_error);
551 cached_nexe_file_->Delete(cb);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000552 return;
553 }
554 // Rename the cached_nexe_file_ file to the cache id, to finalize.
555 pp::CompletionCallback cb =
556 callback_factory_.NewCallback(&PnaclCoordinator::NexeFileWasRenamed);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000557 cached_nexe_file_->Rename(pnacl_options_.GetCacheKey(), cb);
558}
559
560void PnaclCoordinator::CorruptCacheFileWasDeleted(int32_t delete_pp_error,
561 int32_t orig_pp_error) {
562 if (delete_pp_error != PP_OK) {
563 // The cache file was certainly already opened by the time we tried
564 // to write to it, so it should certainly be deletable.
565 PLUGIN_PRINTF(("PnaclCoordinator::CorruptCacheFileWasDeleted "
Ben Murdochbbcdd452013-07-25 10:06:34 +0100566 "delete failed with pp_error=%" NACL_PRId32 "\n",
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000567 delete_pp_error));
568 // fall through and report the original error.
569 }
570 // Report the original error that caused us to consider the
571 // cache file corrupted.
572 if (orig_pp_error == PP_ERROR_NOQUOTA) {
573 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOQUOTA,
574 orig_pp_error,
575 "Failed to copy translated nexe to cache (no quota).");
576 return;
577 }
578 if (orig_pp_error == PP_ERROR_NOSPACE) {
579 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_NOSPACE,
580 orig_pp_error,
581 "Failed to copy translated nexe to cache (no space).");
582 return;
583 }
584 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_COPY_OTHER,
585 orig_pp_error,
586 "Failed to copy translated nexe to cache.");
587 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000588}
589
590void PnaclCoordinator::NexeFileWasRenamed(int32_t pp_error) {
591 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100592 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000593 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000594 if (pp_error == PP_ERROR_NOACCESS) {
595 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_NOACCESS,
596 pp_error,
597 "Failed to finalize cached translation (no access).");
598 return;
599 } else if (pp_error != PP_ERROR_FILEEXISTS) {
600 ReportPpapiError(ERROR_PNACL_CACHE_FINALIZE_RENAME_OTHER,
601 pp_error,
602 "Failed to finalize cached translation.");
603 return;
604 } else { // pp_error == PP_ERROR_FILEEXISTS.
605 // NOTE: if the file already existed, it looks like the rename will
606 // happily succeed. However, we should add a test for this.
607 // Could be a hash collision, or it could also be two tabs racing to
608 // translate the same pexe. We may want UMA stats to know if this happens.
609 // For now, assume that it is a race and try to continue.
610 // If there is truly a corrupted file, then sel_ldr should prevent the
611 // file from loading due to the file size not matching the ELF header.
612 PLUGIN_PRINTF(("PnaclCoordinator::NexeFileWasRenamed file existed\n"));
613 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000614 }
615
616 cached_nexe_file_->FinishRename();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000617
618 int64_t total_time = NaClGetTimeOfDayMicroseconds() - pnacl_init_time_;
619 HistogramTime("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
620 total_time / NACL_MICROS_PER_MILLI);
621 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
622 pexe_size_ / 1024.0,
623 total_time / 1000000.0);
624
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000625 // Open the cache file for reading.
626 pp::CompletionCallback cb =
627 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
628 cached_nexe_file_->OpenRead(cb);
629}
630
631void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) {
632 PLUGIN_PRINTF(("PnaclCoordinator::NexeReadDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100633 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000634 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000635 if (pp_error == PP_ERROR_FILENOTFOUND) {
636 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOTFOUND,
637 pp_error,
638 "Failed to open translated nexe (not found).");
639 return;
640 }
641 if (pp_error == PP_ERROR_NOACCESS) {
642 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_NOACCESS,
643 pp_error,
644 "Failed to open translated nexe (no access).");
645 return;
646 }
647 ReportPpapiError(ERROR_PNACL_CACHE_FETCH_OTHER,
648 pp_error,
649 "Failed to open translated nexe.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000650 return;
651 }
652
653 // Transfer ownership of cache/temp file's wrapper to the coordinator.
654 if (cached_nexe_file_ != NULL) {
655 translated_fd_.reset(cached_nexe_file_->release_read_wrapper());
656 } else {
657 translated_fd_.reset(temp_nexe_file_->release_read_wrapper());
658 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000659 translate_notify_callback_.Run(pp_error);
660}
661
Ben Murdoch2385ea32013-08-06 11:01:04 +0100662void PnaclCoordinator::DidCheckPnaclInstalled(int32_t pp_error) {
663 if (pp_error != PP_OK) {
664 ReportNonPpapiError(
665 ERROR_PNACL_RESOURCE_FETCH,
666 nacl::string("The Portable Native Client component is not installed"
667 " or has been disabled."));
668 return;
669 }
670
671 // Loading resources (e.g. llc and ld nexes) is done with PnaclResources.
672 resources_.reset(new PnaclResources(plugin_,
673 this,
674 this->manifest_.get()));
675 CHECK(resources_ != NULL);
676
677 // The first step of loading resources: read the resource info file.
678 pp::CompletionCallback resource_info_read_cb =
679 callback_factory_.NewCallback(
680 &PnaclCoordinator::ResourceInfoWasRead);
681 resources_->ReadResourceInfo(PnaclUrls::GetResourceInfoUrl(),
682 resource_info_read_cb);
683}
684
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100685void PnaclCoordinator::ResourceInfoWasRead(int32_t pp_error) {
686 PLUGIN_PRINTF(("PluginCoordinator::ResourceInfoWasRead (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100687 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100688 // Second step of loading resources: call StartLoad.
689 pp::CompletionCallback resources_cb =
690 callback_factory_.NewCallback(&PnaclCoordinator::ResourcesDidLoad);
691 resources_->StartLoad(resources_cb);
692}
693
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000694void PnaclCoordinator::ResourcesDidLoad(int32_t pp_error) {
695 PLUGIN_PRINTF(("PnaclCoordinator::ResourcesDidLoad (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100696 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000697 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000698 // Finer-grained error code should have already been reported by
699 // the PnaclResources class.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000700 return;
701 }
702
703 if (!off_the_record_) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100704 if (use_new_cache_) {
705 OpenBitcodeStream();
706 } else {
707 // Open the local temporary FS to see if we get a hit in the cache.
708 pp::CompletionCallback cb =
709 callback_factory_.NewCallback(&PnaclCoordinator::FileSystemDidOpen);
710 int32_t open_error = file_system_->Open(0, cb);
711 if (open_error != PP_OK_COMPLETIONPENDING) {
712 // At this point, no async request has kicked off to check for
713 // permissions, space, etc., so the only error that can be detected
714 // now is that an open() is already in progress (or a really terrible
715 // error).
716 if (pp_error == PP_ERROR_INPROGRESS) {
717 ReportPpapiError(
718 ERROR_PNACL_CACHE_OPEN_INPROGRESS,
719 pp_error,
720 "File system for PNaCl translation cache failed to open "
721 "(in progress).");
722 return;
723 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000724 ReportPpapiError(
Ben Murdocheb525c52013-07-10 11:40:50 +0100725 ERROR_PNACL_CACHE_OPEN_OTHER,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000726 pp_error,
Ben Murdocheb525c52013-07-10 11:40:50 +0100727 "File system for PNaCl translation cache failed to open.");
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000728 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000729 }
730 } else {
731 // We don't have a cache, so do the non-cached codepath.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100732 OpenBitcodeStream();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000733 }
734}
735
736void PnaclCoordinator::FileSystemDidOpen(int32_t pp_error) {
737 PLUGIN_PRINTF(("PnaclCoordinator::FileSystemDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100738 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000739 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000740 if (pp_error == PP_ERROR_NOACCESS) {
741 ReportPpapiError(
742 ERROR_PNACL_CACHE_OPEN_NOACCESS,
743 pp_error,
744 "File system for PNaCl translation cache failed to open "
745 "(no access).");
746 return;
747 }
748 if (pp_error == PP_ERROR_NOQUOTA) {
749 ReportPpapiError(
750 ERROR_PNACL_CACHE_OPEN_NOQUOTA,
751 pp_error,
752 "File system for PNaCl translation cache failed to open "
753 "(no quota).");
754 return;
755 }
756 if (pp_error == PP_ERROR_NOSPACE) {
757 ReportPpapiError(
758 ERROR_PNACL_CACHE_OPEN_NOSPACE,
759 pp_error,
760 "File system for PNaCl translation cache failed to open "
761 "(no space).");
762 return;
763 }
764 ReportPpapiError(ERROR_PNACL_CACHE_OPEN_OTHER,
765 pp_error,
766 "File system for PNaCl translation cache failed to open.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000767 }
768 dir_ref_.reset(new pp::FileRef(*file_system_, kPnaclTempDir));
769 // Attempt to create the directory.
770 pp::CompletionCallback cb =
771 callback_factory_.NewCallback(&PnaclCoordinator::DirectoryWasCreated);
772 dir_ref_->MakeDirectory(cb);
773}
774
775void PnaclCoordinator::DirectoryWasCreated(int32_t pp_error) {
776 PLUGIN_PRINTF(("PnaclCoordinator::DirectoryWasCreated (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100777 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000778 if (pp_error != PP_ERROR_FILEEXISTS && pp_error != PP_OK) {
779 // Directory did not exist and could not be created.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000780 if (pp_error == PP_ERROR_NOACCESS) {
781 ReportPpapiError(
782 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
783 pp_error,
784 "PNaCl translation cache directory creation/check failed "
785 "(no access).");
786 return;
787 }
788 ReportPpapiError(
789 ERROR_PNACL_CACHE_DIRECTORY_CREATE,
790 pp_error,
791 "PNaCl translation cache directory creation/check failed.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000792 return;
793 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100794 OpenBitcodeStream();
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000795}
796
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100797void PnaclCoordinator::OpenBitcodeStream() {
798 // Now open the pexe stream.
799 streaming_downloader_.reset(new FileDownloader());
800 streaming_downloader_->Initialize(plugin_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000801
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100802 // Even though we haven't started downloading, create the translation
803 // thread object immediately. This ensures that any pieces of the file
804 // that get downloaded before the compilation thread is accepting
805 // SRPCs won't get dropped.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000806 translate_thread_.reset(new PnaclTranslateThread());
807 if (translate_thread_ == NULL) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100808 ReportNonPpapiError(
809 ERROR_PNACL_THREAD_CREATE,
810 "PnaclCoordinator: could not allocate translation thread.");
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000811 return;
812 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100813 if (!use_new_cache_) {
814 // We also want to open the object file now so the
815 // translator can start writing to it during streaming translation.
816 obj_file_.reset(new TempFile(plugin_));
817 pp::CompletionCallback obj_cb =
818 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
819 obj_file_->Open(obj_cb, true);
820 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000821
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000822 pp::CompletionCallback cb =
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100823 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidOpen);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000824 if (!streaming_downloader_->OpenStream(pexe_url_, cb, this)) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100825 ReportNonPpapiError(
826 ERROR_PNACL_PEXE_FETCH_OTHER,
827 nacl::string("PnaclCoordinator: failed to open stream ") + pexe_url_);
828 return;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000829 }
830}
831
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100832void PnaclCoordinator::BitcodeStreamDidOpen(int32_t pp_error) {
833 if (pp_error != PP_OK) {
834 BitcodeStreamDidFinish(pp_error);
Ben Murdocheb525c52013-07-10 11:40:50 +0100835 // In the new cache case, we have not spun up the translation process yet,
836 // so we need to call TranslateFinished here.
837 if (use_new_cache_)
838 TranslateFinished(pp_error);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100839 return;
840 }
841
842 if (!off_the_record_) {
843 // Get the cache key and try to open an existing entry.
844 nacl::string headers = streaming_downloader_->GetResponseHeaders();
845 NaClHttpResponseHeaders parser;
846 parser.Parse(headers);
847 nacl::string cache_validators = parser.GetCacheValidators();
848 if (parser.CacheControlNoStore() || cache_validators.empty()) {
849 // We can't cache in this case.
850 pnacl_options_.set_cache_validators("");
851 CachedFileDidOpen(PP_ERROR_FAILED);
852 return;
853 } else {
854 nacl::string url = streaming_downloader_->url();
855 // For now, combine the cache_validators + the URL as the key.
856 // When we change the cache backend to be not-origin-specific
857 // we should send the URL separately, and check in the browser's
858 // RenderViewHost / SiteInstance's IsSameWebsite() to prevent
859 // people from forging the URL for a different origin.
860 pnacl_options_.set_cache_validators(cache_validators + url);
861 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100862 if (use_new_cache_) {
863 pp::CompletionCallback cb =
864 callback_factory_.NewCallback(&PnaclCoordinator::NexeFdDidOpen);
865 int32_t nexe_fd_err =
866 plugin_->nacl_interface()->GetNexeFd(
867 plugin_->pp_instance(),
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100868 streaming_downloader_->url().c_str(),
869 // TODO(dschuff): use the right abi version here.
870 0,
871 pnacl_options_.opt_level(),
872 parser.GetHeader("last-modified").c_str(),
873 parser.GetHeader("etag").c_str(),
Ben Murdocheb525c52013-07-10 11:40:50 +0100874 &is_cache_hit_,
875 &nexe_handle_,
876 cb.pp_completion_callback());
877 if (nexe_fd_err < PP_OK_COMPLETIONPENDING) {
878 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, nexe_fd_err,
879 nacl::string("Call to GetNexeFd failed"));
880 return;
881 }
882 } else {
883 cached_nexe_file_.reset(new LocalTempFile(
884 plugin_, file_system_.get(),
885 nacl::string(kPnaclTempDir),
886 pnacl_options_.GetCacheKey()));
887 pp::CompletionCallback cb =
888 callback_factory_.NewCallback(&PnaclCoordinator::CachedFileDidOpen);
889 cached_nexe_file_->OpenRead(cb);
890 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100891 } else {
892 // No cache case.
893 CachedFileDidOpen(PP_ERROR_FAILED);
894 }
895}
896
Ben Murdocheb525c52013-07-10 11:40:50 +0100897void PnaclCoordinator::NexeFdDidOpen(int32_t pp_error) {
898 PLUGIN_PRINTF(("PnaclCoordinator::NexeFdDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100899 NACL_PRId32 ", hit=%d, handle=%d)\n", pp_error,
Ben Murdocheb525c52013-07-10 11:40:50 +0100900 is_cache_hit_ == PP_TRUE,
901 nexe_handle_));
902 if (pp_error < PP_OK) {
903 ReportPpapiError(ERROR_PNACL_CREATE_TEMP, pp_error,
904 nacl::string("GetNexeFd failed"));
905 return;
906 }
907 temp_nexe_file_.reset(new TempFile(plugin_));
908 if (!temp_nexe_file_->SetExistingFd(nexe_handle_)) {
909 ReportNonPpapiError(
910 ERROR_PNACL_CREATE_TEMP,
911 nacl::string(
912 "PnaclCoordinator: Got bad temp file handle from GetNexeFd"));
913 return;
914 }
915 if (is_cache_hit_ == PP_TRUE) {
916 // Cache hit -- no need to stream the rest of the file.
917 streaming_downloader_.reset(NULL);
918 // TODO(dschuff): update UMA stats for hit/miss once there could actually
919 // be hits/misses.
920 // Open it for reading as the cached nexe file.
921 pp::CompletionCallback cb =
922 callback_factory_.NewCallback(&PnaclCoordinator::NexeReadDidOpen);
923 temp_nexe_file_->Open(cb, false);
924 } else {
925 // Open an object file first so the translator can start writing to it
926 // during streaming translation.
927 obj_file_.reset(new TempFile(plugin_));
928 pp::CompletionCallback obj_cb =
929 callback_factory_.NewCallback(&PnaclCoordinator::ObjectFileDidOpen);
930 obj_file_->Open(obj_cb, true);
931
932 // Meanwhile, a miss means we know we need to stream the bitcode, so stream
933 // the rest of it now. (Calling FinishStreaming means that the downloader
934 // will begin handing data to the coordinator, which is safe any time after
935 // the translate_thread_ object has been initialized).
936 pp::CompletionCallback finish_cb = callback_factory_.NewCallback(
937 &PnaclCoordinator::BitcodeStreamDidFinish);
938 streaming_downloader_->FinishStreaming(finish_cb);
939 }
940}
941
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100942void PnaclCoordinator::CachedFileDidOpen(int32_t pp_error) {
943 PLUGIN_PRINTF(("PnaclCoordinator::CachedFileDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100944 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100945 if (pp_error == PP_OK) {
946 // Cache hit -- no need to stream the rest of the file.
947 streaming_downloader_.reset(NULL);
948 HistogramEnumerateTranslationCache(true);
949 NexeReadDidOpen(PP_OK);
950 return;
951 }
952 // Otherwise, the cache file is missing so we must translate.
953 HistogramEnumerateTranslationCache(false);
954
955 // Continue streaming.
956 pp::CompletionCallback cb =
957 callback_factory_.NewCallback(&PnaclCoordinator::BitcodeStreamDidFinish);
958 streaming_downloader_->FinishStreaming(cb);
959}
960
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000961void PnaclCoordinator::BitcodeStreamDidFinish(int32_t pp_error) {
962 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamDidFinish (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100963 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000964 if (pp_error != PP_OK) {
965 // Defer reporting the error and cleanup until after the translation
966 // thread returns, because it may be accessing the coordinator's
967 // objects or writing to the files.
968 translate_finish_error_ = pp_error;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000969 if (pp_error == PP_ERROR_ABORTED) {
970 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_ABORTED,
971 "PnaclCoordinator: pexe load failed (aborted).");
972 }
973 if (pp_error == PP_ERROR_NOACCESS) {
974 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_NOACCESS,
975 "PnaclCoordinator: pexe load failed (no access).");
976 } else {
977 nacl::stringstream ss;
978 ss << "PnaclCoordinator: pexe load failed (pp_error=" << pp_error << ").";
979 error_info_.SetReport(ERROR_PNACL_PEXE_FETCH_OTHER, ss.str());
980 }
Ben Murdochbb1529c2013-08-08 10:24:53 +0100981 if (use_new_cache_) {
982 plugin_->nacl_interface()->ReportTranslationFinished(
983 plugin_->pp_instance(),
984 PP_FALSE);
985 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000986 translate_thread_->AbortSubprocesses();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000987 } else {
988 // Compare download completion pct (100% now), to compile completion pct.
989 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
990 pexe_bytes_compiled_, pexe_size_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000991 }
992}
993
994void PnaclCoordinator::BitcodeStreamGotData(int32_t pp_error,
995 FileStreamData data) {
996 PLUGIN_PRINTF(("PnaclCoordinator::BitcodeStreamGotData (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +0100997 NACL_PRId32 ", data=%p)\n", pp_error, data ? &(*data)[0] : 0));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000998 DCHECK(translate_thread_.get());
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100999
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001000 translate_thread_->PutBytes(data, pp_error);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001001 // If pp_error > 0, then it represents the number of bytes received.
1002 if (data && pp_error > 0) {
1003 pexe_size_ += pp_error;
1004 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001005}
1006
1007StreamCallback PnaclCoordinator::GetCallback() {
1008 return callback_factory_.NewCallbackWithOutput(
1009 &PnaclCoordinator::BitcodeStreamGotData);
1010}
1011
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001012void PnaclCoordinator::BitcodeGotCompiled(int32_t pp_error,
1013 int64_t bytes_compiled) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001014 pexe_bytes_compiled_ += bytes_compiled;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +01001015 // If we don't know the expected total yet, ask.
1016 if (!ExpectedProgressKnown()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001017 int64_t amount_downloaded; // dummy variable.
1018 streaming_downloader_->GetDownloadProgress(&amount_downloaded,
1019 &expected_pexe_size_);
1020 }
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +01001021 // Hold off reporting the last few bytes of progress, since we don't know
1022 // when they are actually completely compiled. "bytes_compiled" only means
1023 // that bytes were sent to the compiler.
1024 if (ExpectedProgressKnown()) {
1025 if (!ShouldDelayProgressEvent()) {
1026 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1027 pexe_url_,
1028 plugin::Plugin::LENGTH_IS_COMPUTABLE,
1029 pexe_bytes_compiled_,
1030 expected_pexe_size_);
1031 }
1032 } else {
1033 plugin_->EnqueueProgressEvent(plugin::Plugin::kProgressEventProgress,
1034 pexe_url_,
1035 plugin::Plugin::LENGTH_IS_NOT_COMPUTABLE,
1036 pexe_bytes_compiled_,
1037 expected_pexe_size_);
1038 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001039}
1040
1041pp::CompletionCallback PnaclCoordinator::GetCompileProgressCallback(
1042 int64_t bytes_compiled) {
1043 return callback_factory_.NewCallback(&PnaclCoordinator::BitcodeGotCompiled,
1044 bytes_compiled);
1045}
1046
1047void PnaclCoordinator::GetCurrentProgress(int64_t* bytes_loaded,
1048 int64_t* bytes_total) {
1049 *bytes_loaded = pexe_bytes_compiled_;
1050 *bytes_total = expected_pexe_size_;
1051}
1052
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001053void PnaclCoordinator::ObjectFileDidOpen(int32_t pp_error) {
1054 PLUGIN_PRINTF(("PnaclCoordinator::ObjectFileDidOpen (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001055 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001056 if (pp_error != PP_OK) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001057 ReportPpapiError(ERROR_PNACL_CREATE_TEMP,
1058 pp_error,
1059 "Failed to open scratch object file.");
Ben Murdochbb1529c2013-08-08 10:24:53 +01001060 if (use_new_cache_) {
1061 plugin_->nacl_interface()->ReportTranslationFinished(
1062 plugin_->pp_instance(),
1063 PP_FALSE);
1064 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001065 return;
1066 }
Ben Murdocheb525c52013-07-10 11:40:50 +01001067 // Open the nexe file for connecting ld and sel_ldr.
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001068 // Start translation when done with this last step of setup!
Ben Murdocheb525c52013-07-10 11:40:50 +01001069 if (!use_new_cache_)
1070 // In the new cache case, the TempFile has already been created.
1071 temp_nexe_file_.reset(new TempFile(plugin_));
1072
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001073 pp::CompletionCallback cb =
1074 callback_factory_.NewCallback(&PnaclCoordinator::RunTranslate);
Ben Murdocheb525c52013-07-10 11:40:50 +01001075 temp_nexe_file_->Open(cb, true);
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001076}
1077
1078void PnaclCoordinator::RunTranslate(int32_t pp_error) {
1079 PLUGIN_PRINTF(("PnaclCoordinator::RunTranslate (pp_error=%"
Ben Murdochbbcdd452013-07-25 10:06:34 +01001080 NACL_PRId32 ")\n", pp_error));
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001081 // Invoke llc followed by ld off the main thread. This allows use of
1082 // blocking RPCs that would otherwise block the JavaScript main thread.
1083 pp::CompletionCallback report_translate_finished =
1084 callback_factory_.NewCallback(&PnaclCoordinator::TranslateFinished);
1085
1086 CHECK(translate_thread_ != NULL);
1087 translate_thread_->RunTranslate(report_translate_finished,
1088 manifest_.get(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001089 obj_file_.get(),
1090 temp_nexe_file_.get(),
1091 &error_info_,
1092 resources_.get(),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001093 &pnacl_options_,
1094 this,
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001095 plugin_);
1096}
1097
1098} // namespace plugin