1// Copyright 2006-2008 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <errno.h> 6#include <stdio.h> 7#ifdef COMPRESS_STARTUP_DATA_BZ2 8#include <bzlib.h> 9#endif 10#include <signal.h> 11 12#include "src/v8.h" 13 14#include "include/libplatform/libplatform.h" 15#include "src/assembler.h" 16#include "src/base/platform/platform.h" 17#include "src/bootstrapper.h" 18#include "src/flags.h" 19#include "src/list.h" 20#include "src/natives.h" 21#include "src/serialize.h" 22 23 24using namespace v8; 25 26 27class Compressor { 28 public: 29 virtual ~Compressor() {} 30 virtual bool Compress(i::Vector<i::byte> input) = 0; 31 virtual i::Vector<i::byte>* output() = 0; 32}; 33 34 35class SnapshotWriter { 36 public: 37 explicit SnapshotWriter(const char* snapshot_file) 38 : fp_(GetFileDescriptorOrDie(snapshot_file)) 39 , raw_file_(NULL) 40 , raw_context_file_(NULL) 41 , startup_blob_file_(NULL) 42 , compressor_(NULL) { 43 } 44 45 ~SnapshotWriter() { 46 fclose(fp_); 47 if (raw_file_) fclose(raw_file_); 48 if (raw_context_file_) fclose(raw_context_file_); 49 if (startup_blob_file_) fclose(startup_blob_file_); 50 } 51 52 void SetCompressor(Compressor* compressor) { 53 compressor_ = compressor; 54 } 55 56 void SetRawFiles(const char* raw_file, const char* raw_context_file) { 57 raw_file_ = GetFileDescriptorOrDie(raw_file); 58 raw_context_file_ = GetFileDescriptorOrDie(raw_context_file); 59 } 60 61 void SetStartupBlobFile(const char* startup_blob_file) { 62 if (startup_blob_file != NULL) 63 startup_blob_file_ = GetFileDescriptorOrDie(startup_blob_file); 64 } 65 66 void WriteSnapshot(const i::List<i::byte>& snapshot_data, 67 const i::Serializer& serializer, 68 const i::List<i::byte>& context_snapshot_data, 69 const i::Serializer& context_serializer) const { 70 WriteSnapshotFile(snapshot_data, serializer, 71 context_snapshot_data, context_serializer); 72 MaybeWriteStartupBlob(snapshot_data, serializer, 73 context_snapshot_data, context_serializer); 74 } 75 76 private: 77 void MaybeWriteStartupBlob(const i::List<i::byte>& snapshot_data, 78 const i::Serializer& serializer, 79 const i::List<i::byte>& context_snapshot_data, 80 const i::Serializer& context_serializer) const { 81 if (!startup_blob_file_) 82 return; 83 84 i::List<i::byte> startup_blob; 85 i::ListSnapshotSink sink(&startup_blob); 86 87 int spaces[] = { 88 i::NEW_SPACE, i::OLD_POINTER_SPACE, i::OLD_DATA_SPACE, i::CODE_SPACE, 89 i::MAP_SPACE, i::CELL_SPACE, i::PROPERTY_CELL_SPACE 90 }; 91 92 i::byte* snapshot_bytes = snapshot_data.begin(); 93 sink.PutBlob(snapshot_bytes, snapshot_data.length(), "snapshot"); 94 for (size_t i = 0; i < arraysize(spaces); ++i) 95 sink.PutInt(serializer.CurrentAllocationAddress(spaces[i]), "spaces"); 96 97 i::byte* context_bytes = context_snapshot_data.begin(); 98 sink.PutBlob(context_bytes, context_snapshot_data.length(), "context"); 99 for (size_t i = 0; i < arraysize(spaces); ++i) 100 sink.PutInt(context_serializer.CurrentAllocationAddress(spaces[i]), 101 "spaces"); 102 103 size_t written = fwrite(startup_blob.begin(), 1, startup_blob.length(), 104 startup_blob_file_); 105 if (written != (size_t)startup_blob.length()) { 106 i::PrintF("Writing snapshot file failed.. Aborting.\n"); 107 exit(1); 108 } 109 } 110 111 void WriteSnapshotFile(const i::List<i::byte>& snapshot_data, 112 const i::Serializer& serializer, 113 const i::List<i::byte>& context_snapshot_data, 114 const i::Serializer& context_serializer) const { 115 WriteFilePrefix(); 116 WriteData("", snapshot_data, raw_file_); 117 WriteData("context_", context_snapshot_data, raw_context_file_); 118 WriteMeta("context_", context_serializer); 119 WriteMeta("", serializer); 120 WriteFileSuffix(); 121 } 122 123 void WriteFilePrefix() const { 124 fprintf(fp_, "// Autogenerated snapshot file. Do not edit.\n\n"); 125 fprintf(fp_, "#include \"src/v8.h\"\n"); 126 fprintf(fp_, "#include \"src/base/platform/platform.h\"\n\n"); 127 fprintf(fp_, "#include \"src/snapshot.h\"\n\n"); 128 fprintf(fp_, "namespace v8 {\n"); 129 fprintf(fp_, "namespace internal {\n\n"); 130 } 131 132 void WriteFileSuffix() const { 133 fprintf(fp_, "} // namespace internal\n"); 134 fprintf(fp_, "} // namespace v8\n"); 135 } 136 137 void WriteData(const char* prefix, const i::List<i::byte>& source_data, 138 FILE* raw_file) const { 139 const i::List<i::byte>* data_to_be_written = NULL; 140 i::List<i::byte> compressed_data; 141 if (!compressor_) { 142 data_to_be_written = &source_data; 143 } else if (compressor_->Compress(source_data.ToVector())) { 144 compressed_data.AddAll(*compressor_->output()); 145 data_to_be_written = &compressed_data; 146 } else { 147 i::PrintF("Compression failed. Aborting.\n"); 148 exit(1); 149 } 150 151 DCHECK(data_to_be_written); 152 MaybeWriteRawFile(data_to_be_written, raw_file); 153 WriteData(prefix, source_data, data_to_be_written); 154 } 155 156 void MaybeWriteRawFile(const i::List<i::byte>* data, FILE* raw_file) const { 157 if (!data || !raw_file) 158 return; 159 160 // Sanity check, whether i::List iterators truly return pointers to an 161 // internal array. 162 DCHECK(data->end() - data->begin() == data->length()); 163 164 size_t written = fwrite(data->begin(), 1, data->length(), raw_file); 165 if (written != (size_t)data->length()) { 166 i::PrintF("Writing raw file failed.. Aborting.\n"); 167 exit(1); 168 } 169 } 170 171 void WriteData(const char* prefix, const i::List<i::byte>& source_data, 172 const i::List<i::byte>* data_to_be_written) const { 173 fprintf(fp_, "const byte Snapshot::%sdata_[] = {\n", prefix); 174 WriteSnapshotData(data_to_be_written); 175 fprintf(fp_, "};\n"); 176 fprintf(fp_, "const int Snapshot::%ssize_ = %d;\n", prefix, 177 data_to_be_written->length()); 178 179 if (data_to_be_written == &source_data) { 180 fprintf(fp_, "const byte* Snapshot::%sraw_data_ = Snapshot::%sdata_;\n", 181 prefix, prefix); 182 fprintf(fp_, "const int Snapshot::%sraw_size_ = Snapshot::%ssize_;\n", 183 prefix, prefix); 184 } else { 185 fprintf(fp_, "const byte* Snapshot::%sraw_data_ = NULL;\n", prefix); 186 fprintf(fp_, "const int Snapshot::%sraw_size_ = %d;\n", 187 prefix, source_data.length()); 188 } 189 fprintf(fp_, "\n"); 190 } 191 192 void WriteMeta(const char* prefix, const i::Serializer& ser) const { 193 WriteSizeVar(ser, prefix, "new", i::NEW_SPACE); 194 WriteSizeVar(ser, prefix, "pointer", i::OLD_POINTER_SPACE); 195 WriteSizeVar(ser, prefix, "data", i::OLD_DATA_SPACE); 196 WriteSizeVar(ser, prefix, "code", i::CODE_SPACE); 197 WriteSizeVar(ser, prefix, "map", i::MAP_SPACE); 198 WriteSizeVar(ser, prefix, "cell", i::CELL_SPACE); 199 WriteSizeVar(ser, prefix, "property_cell", i::PROPERTY_CELL_SPACE); 200 fprintf(fp_, "\n"); 201 } 202 203 void WriteSizeVar(const i::Serializer& ser, const char* prefix, 204 const char* name, int space) const { 205 fprintf(fp_, "const int Snapshot::%s%s_space_used_ = %d;\n", 206 prefix, name, ser.CurrentAllocationAddress(space)); 207 } 208 209 void WriteSnapshotData(const i::List<i::byte>* data) const { 210 for (int i = 0; i < data->length(); i++) { 211 if ((i & 0x1f) == 0x1f) 212 fprintf(fp_, "\n"); 213 if (i > 0) 214 fprintf(fp_, ","); 215 fprintf(fp_, "%u", static_cast<unsigned char>(data->at(i))); 216 } 217 fprintf(fp_, "\n"); 218 } 219 220 FILE* GetFileDescriptorOrDie(const char* filename) { 221 FILE* fp = base::OS::FOpen(filename, "wb"); 222 if (fp == NULL) { 223 i::PrintF("Unable to open file \"%s\" for writing.\n", filename); 224 exit(1); 225 } 226 return fp; 227 } 228 229 FILE* fp_; 230 FILE* raw_file_; 231 FILE* raw_context_file_; 232 FILE* startup_blob_file_; 233 Compressor* compressor_; 234}; 235 236 237#ifdef COMPRESS_STARTUP_DATA_BZ2 238class BZip2Compressor : public Compressor { 239 public: 240 BZip2Compressor() : output_(NULL) {} 241 virtual ~BZip2Compressor() { 242 delete output_; 243 } 244 virtual bool Compress(i::Vector<char> input) { 245 delete output_; 246 output_ = new i::ScopedVector<char>((input.length() * 101) / 100 + 1000); 247 unsigned int output_length_ = output_->length(); 248 int result = BZ2_bzBuffToBuffCompress(output_->start(), &output_length_, 249 input.start(), input.length(), 250 9, 1, 0); 251 if (result == BZ_OK) { 252 output_->Truncate(output_length_); 253 return true; 254 } else { 255 fprintf(stderr, "bzlib error code: %d\n", result); 256 return false; 257 } 258 } 259 virtual i::Vector<char>* output() { return output_; } 260 261 private: 262 i::ScopedVector<char>* output_; 263}; 264 265 266class BZip2Decompressor : public StartupDataDecompressor { 267 public: 268 virtual ~BZip2Decompressor() { } 269 270 protected: 271 virtual int DecompressData(char* raw_data, 272 int* raw_data_size, 273 const char* compressed_data, 274 int compressed_data_size) { 275 DCHECK_EQ(StartupData::kBZip2, 276 V8::GetCompressedStartupDataAlgorithm()); 277 unsigned int decompressed_size = *raw_data_size; 278 int result = 279 BZ2_bzBuffToBuffDecompress(raw_data, 280 &decompressed_size, 281 const_cast<char*>(compressed_data), 282 compressed_data_size, 283 0, 1); 284 if (result == BZ_OK) { 285 *raw_data_size = decompressed_size; 286 } 287 return result; 288 } 289}; 290#endif 291 292 293void DumpException(Handle<Message> message) { 294 String::Utf8Value message_string(message->Get()); 295 String::Utf8Value message_line(message->GetSourceLine()); 296 fprintf(stderr, "%s at line %d\n", *message_string, message->GetLineNumber()); 297 fprintf(stderr, "%s\n", *message_line); 298 for (int i = 0; i <= message->GetEndColumn(); ++i) { 299 fprintf(stderr, "%c", i < message->GetStartColumn() ? ' ' : '^'); 300 } 301 fprintf(stderr, "\n"); 302} 303 304 305int main(int argc, char** argv) { 306 // By default, log code create information in the snapshot. 307 i::FLAG_log_code = true; 308 309 // Print the usage if an error occurs when parsing the command line 310 // flags or if the help flag is set. 311 int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true); 312 if (result > 0 || argc != 2 || i::FLAG_help) { 313 ::printf("Usage: %s [flag] ... outfile\n", argv[0]); 314 i::FlagList::PrintHelp(); 315 return !i::FLAG_help; 316 } 317 318 i::CpuFeatures::Probe(true); 319 V8::InitializeICU(); 320 v8::Platform* platform = v8::platform::CreateDefaultPlatform(); 321 v8::V8::InitializePlatform(platform); 322 v8::V8::Initialize(); 323 324#ifdef COMPRESS_STARTUP_DATA_BZ2 325 BZip2Decompressor natives_decompressor; 326 int bz2_result = natives_decompressor.Decompress(); 327 if (bz2_result != BZ_OK) { 328 fprintf(stderr, "bzip error code: %d\n", bz2_result); 329 exit(1); 330 } 331#endif 332 i::FLAG_logfile_per_isolate = false; 333 334 Isolate::CreateParams params; 335 params.enable_serializer = true; 336 Isolate* isolate = v8::Isolate::New(params); 337 { Isolate::Scope isolate_scope(isolate); 338 i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate); 339 340 Persistent<Context> context; 341 { 342 HandleScope handle_scope(isolate); 343 context.Reset(isolate, Context::New(isolate)); 344 } 345 346 if (context.IsEmpty()) { 347 fprintf(stderr, 348 "\nException thrown while compiling natives - see above.\n\n"); 349 exit(1); 350 } 351 if (i::FLAG_extra_code != NULL) { 352 // Capture 100 frames if anything happens. 353 V8::SetCaptureStackTraceForUncaughtExceptions(true, 100); 354 HandleScope scope(isolate); 355 v8::Context::Scope cscope(v8::Local<v8::Context>::New(isolate, context)); 356 const char* name = i::FLAG_extra_code; 357 FILE* file = base::OS::FOpen(name, "rb"); 358 if (file == NULL) { 359 fprintf(stderr, "Failed to open '%s': errno %d\n", name, errno); 360 exit(1); 361 } 362 363 fseek(file, 0, SEEK_END); 364 int size = ftell(file); 365 rewind(file); 366 367 char* chars = new char[size + 1]; 368 chars[size] = '\0'; 369 for (int i = 0; i < size;) { 370 int read = static_cast<int>(fread(&chars[i], 1, size - i, file)); 371 if (read < 0) { 372 fprintf(stderr, "Failed to read '%s': errno %d\n", name, errno); 373 exit(1); 374 } 375 i += read; 376 } 377 fclose(file); 378 Local<String> source = String::NewFromUtf8(isolate, chars); 379 TryCatch try_catch; 380 Local<Script> script = Script::Compile(source); 381 if (try_catch.HasCaught()) { 382 fprintf(stderr, "Failure compiling '%s'\n", name); 383 DumpException(try_catch.Message()); 384 exit(1); 385 } 386 script->Run(); 387 if (try_catch.HasCaught()) { 388 fprintf(stderr, "Failure running '%s'\n", name); 389 DumpException(try_catch.Message()); 390 exit(1); 391 } 392 } 393 // Make sure all builtin scripts are cached. 394 { HandleScope scope(isolate); 395 for (int i = 0; i < i::Natives::GetBuiltinsCount(); i++) { 396 internal_isolate->bootstrapper()->NativesSourceLookup(i); 397 } 398 } 399 // If we don't do this then we end up with a stray root pointing at the 400 // context even after we have disposed of the context. 401 internal_isolate->heap()->CollectAllGarbage( 402 i::Heap::kNoGCFlags, "mksnapshot"); 403 i::Object* raw_context = *v8::Utils::OpenPersistent(context); 404 context.Reset(); 405 406 // This results in a somewhat smaller snapshot, probably because it gets 407 // rid of some things that are cached between garbage collections. 408 i::List<i::byte> snapshot_data; 409 i::ListSnapshotSink snapshot_sink(&snapshot_data); 410 i::StartupSerializer ser(internal_isolate, &snapshot_sink); 411 ser.SerializeStrongReferences(); 412 413 i::List<i::byte> context_data; 414 i::ListSnapshotSink contex_sink(&context_data); 415 i::PartialSerializer context_ser(internal_isolate, &ser, &contex_sink); 416 context_ser.Serialize(&raw_context); 417 ser.SerializeWeakReferences(); 418 419 { 420 SnapshotWriter writer(argv[1]); 421 if (i::FLAG_raw_file && i::FLAG_raw_context_file) 422 writer.SetRawFiles(i::FLAG_raw_file, i::FLAG_raw_context_file); 423 if (i::FLAG_startup_blob) 424 writer.SetStartupBlobFile(i::FLAG_startup_blob); 425 #ifdef COMPRESS_STARTUP_DATA_BZ2 426 BZip2Compressor bzip2; 427 writer.SetCompressor(&bzip2); 428 #endif 429 writer.WriteSnapshot(snapshot_data, ser, context_data, context_ser); 430 } 431 } 432 433 isolate->Dispose(); 434 V8::Dispose(); 435 V8::ShutdownPlatform(); 436 delete platform; 437 return 0; 438} 439