Home | History | Annotate | Download | only in src
      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 
     24 using namespace v8;
     25 
     26 
     27 class 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 
     35 class 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
    238 class 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 
    266 class 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 
    293 void 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 
    305 int 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