Home | History | Annotate | Download | only in src
      1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include <errno.h>
     29 #include <stdio.h>
     30 #ifdef COMPRESS_STARTUP_DATA_BZ2
     31 #include <bzlib.h>
     32 #endif
     33 #include <signal.h>
     34 
     35 #include "v8.h"
     36 
     37 #include "bootstrapper.h"
     38 #include "flags.h"
     39 #include "natives.h"
     40 #include "platform.h"
     41 #include "serialize.h"
     42 #include "list.h"
     43 
     44 using namespace v8;
     45 
     46 
     47 class Compressor {
     48  public:
     49   virtual ~Compressor() {}
     50   virtual bool Compress(i::Vector<char> input) = 0;
     51   virtual i::Vector<char>* output() = 0;
     52 };
     53 
     54 
     55 class PartialSnapshotSink : public i::SnapshotByteSink {
     56  public:
     57   PartialSnapshotSink() : data_(), raw_size_(-1) { }
     58   virtual ~PartialSnapshotSink() { data_.Free(); }
     59   virtual void Put(int byte, const char* description) {
     60     data_.Add(byte);
     61   }
     62   virtual int Position() { return data_.length(); }
     63   void Print(FILE* fp) {
     64     int length = Position();
     65     for (int j = 0; j < length; j++) {
     66       if ((j & 0x1f) == 0x1f) {
     67         fprintf(fp, "\n");
     68       }
     69       if (j != 0) {
     70         fprintf(fp, ",");
     71       }
     72       fprintf(fp, "%u", static_cast<unsigned char>(at(j)));
     73     }
     74   }
     75   char at(int i) { return data_[i]; }
     76   bool Compress(Compressor* compressor) {
     77     ASSERT_EQ(-1, raw_size_);
     78     raw_size_ = data_.length();
     79     if (!compressor->Compress(data_.ToVector())) return false;
     80     data_.Clear();
     81     data_.AddAll(*compressor->output());
     82     return true;
     83   }
     84   int raw_size() { return raw_size_; }
     85 
     86  private:
     87   i::List<char> data_;
     88   int raw_size_;
     89 };
     90 
     91 
     92 class CppByteSink : public PartialSnapshotSink {
     93  public:
     94   explicit CppByteSink(const char* snapshot_file) {
     95     fp_ = i::OS::FOpen(snapshot_file, "wb");
     96     if (fp_ == NULL) {
     97       i::PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
     98       exit(1);
     99     }
    100     fprintf(fp_, "// Autogenerated snapshot file. Do not edit.\n\n");
    101     fprintf(fp_, "#include \"v8.h\"\n");
    102     fprintf(fp_, "#include \"platform.h\"\n\n");
    103     fprintf(fp_, "#include \"snapshot.h\"\n\n");
    104     fprintf(fp_, "namespace v8 {\nnamespace internal {\n\n");
    105     fprintf(fp_, "const byte Snapshot::data_[] = {");
    106   }
    107 
    108   virtual ~CppByteSink() {
    109     fprintf(fp_, "const int Snapshot::size_ = %d;\n", Position());
    110 #ifdef COMPRESS_STARTUP_DATA_BZ2
    111     fprintf(fp_, "const byte* Snapshot::raw_data_ = NULL;\n");
    112     fprintf(fp_,
    113             "const int Snapshot::raw_size_ = %d;\n\n",
    114             raw_size());
    115 #else
    116     fprintf(fp_,
    117             "const byte* Snapshot::raw_data_ = Snapshot::data_;\n");
    118     fprintf(fp_,
    119             "const int Snapshot::raw_size_ = Snapshot::size_;\n\n");
    120 #endif
    121     fprintf(fp_, "} }  // namespace v8::internal\n");
    122     fclose(fp_);
    123   }
    124 
    125   void WriteSpaceUsed(
    126       const char* prefix,
    127       int new_space_used,
    128       int pointer_space_used,
    129       int data_space_used,
    130       int code_space_used,
    131       int map_space_used,
    132       int cell_space_used,
    133       int property_cell_space_used) {
    134     fprintf(fp_,
    135             "const int Snapshot::%snew_space_used_ = %d;\n",
    136             prefix,
    137             new_space_used);
    138     fprintf(fp_,
    139             "const int Snapshot::%spointer_space_used_ = %d;\n",
    140             prefix,
    141             pointer_space_used);
    142     fprintf(fp_,
    143             "const int Snapshot::%sdata_space_used_ = %d;\n",
    144             prefix,
    145             data_space_used);
    146     fprintf(fp_,
    147             "const int Snapshot::%scode_space_used_ = %d;\n",
    148             prefix,
    149             code_space_used);
    150     fprintf(fp_,
    151             "const int Snapshot::%smap_space_used_ = %d;\n",
    152             prefix,
    153             map_space_used);
    154     fprintf(fp_,
    155             "const int Snapshot::%scell_space_used_ = %d;\n",
    156             prefix,
    157             cell_space_used);
    158     fprintf(fp_,
    159             "const int Snapshot::%sproperty_cell_space_used_ = %d;\n",
    160             prefix,
    161             property_cell_space_used);
    162   }
    163 
    164   void WritePartialSnapshot() {
    165     int length = partial_sink_.Position();
    166     fprintf(fp_, "};\n\n");
    167     fprintf(fp_, "const int Snapshot::context_size_ = %d;\n",  length);
    168 #ifdef COMPRESS_STARTUP_DATA_BZ2
    169     fprintf(fp_,
    170             "const int Snapshot::context_raw_size_ = %d;\n",
    171             partial_sink_.raw_size());
    172 #else
    173     fprintf(fp_,
    174             "const int Snapshot::context_raw_size_ = "
    175             "Snapshot::context_size_;\n");
    176 #endif
    177     fprintf(fp_, "const byte Snapshot::context_data_[] = {\n");
    178     partial_sink_.Print(fp_);
    179     fprintf(fp_, "};\n\n");
    180 #ifdef COMPRESS_STARTUP_DATA_BZ2
    181     fprintf(fp_, "const byte* Snapshot::context_raw_data_ = NULL;\n");
    182 #else
    183     fprintf(fp_, "const byte* Snapshot::context_raw_data_ ="
    184             " Snapshot::context_data_;\n");
    185 #endif
    186   }
    187 
    188   void WriteSnapshot() {
    189     Print(fp_);
    190   }
    191 
    192   PartialSnapshotSink* partial_sink() { return &partial_sink_; }
    193 
    194  private:
    195   FILE* fp_;
    196   PartialSnapshotSink partial_sink_;
    197 };
    198 
    199 
    200 #ifdef COMPRESS_STARTUP_DATA_BZ2
    201 class BZip2Compressor : public Compressor {
    202  public:
    203   BZip2Compressor() : output_(NULL) {}
    204   virtual ~BZip2Compressor() {
    205     delete output_;
    206   }
    207   virtual bool Compress(i::Vector<char> input) {
    208     delete output_;
    209     output_ = new i::ScopedVector<char>((input.length() * 101) / 100 + 1000);
    210     unsigned int output_length_ = output_->length();
    211     int result = BZ2_bzBuffToBuffCompress(output_->start(), &output_length_,
    212                                           input.start(), input.length(),
    213                                           9, 1, 0);
    214     if (result == BZ_OK) {
    215       output_->Truncate(output_length_);
    216       return true;
    217     } else {
    218       fprintf(stderr, "bzlib error code: %d\n", result);
    219       return false;
    220     }
    221   }
    222   virtual i::Vector<char>* output() { return output_; }
    223 
    224  private:
    225   i::ScopedVector<char>* output_;
    226 };
    227 
    228 
    229 class BZip2Decompressor : public StartupDataDecompressor {
    230  public:
    231   virtual ~BZip2Decompressor() { }
    232 
    233  protected:
    234   virtual int DecompressData(char* raw_data,
    235                              int* raw_data_size,
    236                              const char* compressed_data,
    237                              int compressed_data_size) {
    238     ASSERT_EQ(StartupData::kBZip2,
    239               V8::GetCompressedStartupDataAlgorithm());
    240     unsigned int decompressed_size = *raw_data_size;
    241     int result =
    242         BZ2_bzBuffToBuffDecompress(raw_data,
    243                                    &decompressed_size,
    244                                    const_cast<char*>(compressed_data),
    245                                    compressed_data_size,
    246                                    0, 1);
    247     if (result == BZ_OK) {
    248       *raw_data_size = decompressed_size;
    249     }
    250     return result;
    251   }
    252 };
    253 #endif
    254 
    255 
    256 void DumpException(Handle<Message> message) {
    257   String::Utf8Value message_string(message->Get());
    258   String::Utf8Value message_line(message->GetSourceLine());
    259   fprintf(stderr, "%s at line %d\n", *message_string, message->GetLineNumber());
    260   fprintf(stderr, "%s\n", *message_line);
    261   for (int i = 0; i <= message->GetEndColumn(); ++i) {
    262     fprintf(stderr, "%c", i < message->GetStartColumn() ? ' ' : '^');
    263   }
    264   fprintf(stderr, "\n");
    265 }
    266 
    267 
    268 int main(int argc, char** argv) {
    269   V8::InitializeICU();
    270   i::Isolate::SetCrashIfDefaultIsolateInitialized();
    271 
    272   // By default, log code create information in the snapshot.
    273   i::FLAG_log_code = true;
    274 
    275   // Print the usage if an error occurs when parsing the command line
    276   // flags or if the help flag is set.
    277   int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
    278   if (result > 0 || argc != 2 || i::FLAG_help) {
    279     ::printf("Usage: %s [flag] ... outfile\n", argv[0]);
    280     i::FlagList::PrintHelp();
    281     return !i::FLAG_help;
    282   }
    283 #ifdef COMPRESS_STARTUP_DATA_BZ2
    284   BZip2Decompressor natives_decompressor;
    285   int bz2_result = natives_decompressor.Decompress();
    286   if (bz2_result != BZ_OK) {
    287     fprintf(stderr, "bzip error code: %d\n", bz2_result);
    288     exit(1);
    289   }
    290 #endif
    291   i::FLAG_logfile_per_isolate = false;
    292 
    293   Isolate* isolate = v8::Isolate::New();
    294   isolate->Enter();
    295   i::Isolate* internal_isolate = reinterpret_cast<i::Isolate*>(isolate);
    296   i::Serializer::Enable(internal_isolate);
    297   Persistent<Context> context;
    298   {
    299     HandleScope handle_scope(isolate);
    300     context.Reset(isolate, Context::New(isolate));
    301   }
    302 
    303   if (context.IsEmpty()) {
    304     fprintf(stderr,
    305             "\nException thrown while compiling natives - see above.\n\n");
    306     exit(1);
    307   }
    308   if (i::FLAG_extra_code != NULL) {
    309     // Capture 100 frames if anything happens.
    310     V8::SetCaptureStackTraceForUncaughtExceptions(true, 100);
    311     HandleScope scope(isolate);
    312     v8::Context::Scope cscope(v8::Local<v8::Context>::New(isolate, context));
    313     const char* name = i::FLAG_extra_code;
    314     FILE* file = i::OS::FOpen(name, "rb");
    315     if (file == NULL) {
    316       fprintf(stderr, "Failed to open '%s': errno %d\n", name, errno);
    317       exit(1);
    318     }
    319 
    320     fseek(file, 0, SEEK_END);
    321     int size = ftell(file);
    322     rewind(file);
    323 
    324     char* chars = new char[size + 1];
    325     chars[size] = '\0';
    326     for (int i = 0; i < size;) {
    327       int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    328       if (read < 0) {
    329         fprintf(stderr, "Failed to read '%s': errno %d\n", name, errno);
    330         exit(1);
    331       }
    332       i += read;
    333     }
    334     fclose(file);
    335     Local<String> source = String::NewFromUtf8(isolate, chars);
    336     TryCatch try_catch;
    337     Local<Script> script = Script::Compile(source);
    338     if (try_catch.HasCaught()) {
    339       fprintf(stderr, "Failure compiling '%s'\n", name);
    340       DumpException(try_catch.Message());
    341       exit(1);
    342     }
    343     script->Run();
    344     if (try_catch.HasCaught()) {
    345       fprintf(stderr, "Failure running '%s'\n", name);
    346       DumpException(try_catch.Message());
    347       exit(1);
    348     }
    349   }
    350   // Make sure all builtin scripts are cached.
    351   { HandleScope scope(isolate);
    352     for (int i = 0; i < i::Natives::GetBuiltinsCount(); i++) {
    353       internal_isolate->bootstrapper()->NativesSourceLookup(i);
    354     }
    355   }
    356   // If we don't do this then we end up with a stray root pointing at the
    357   // context even after we have disposed of the context.
    358   internal_isolate->heap()->CollectAllGarbage(
    359       i::Heap::kNoGCFlags, "mksnapshot");
    360   i::Object* raw_context = *v8::Utils::OpenPersistent(context);
    361   context.Reset();
    362   CppByteSink sink(argv[1]);
    363   // This results in a somewhat smaller snapshot, probably because it gets rid
    364   // of some things that are cached between garbage collections.
    365   i::StartupSerializer ser(internal_isolate, &sink);
    366   ser.SerializeStrongReferences();
    367 
    368   i::PartialSerializer partial_ser(
    369       internal_isolate, &ser, sink.partial_sink());
    370   partial_ser.Serialize(&raw_context);
    371 
    372   ser.SerializeWeakReferences();
    373 
    374 #ifdef COMPRESS_STARTUP_DATA_BZ2
    375   BZip2Compressor compressor;
    376   if (!sink.Compress(&compressor))
    377     return 1;
    378   if (!sink.partial_sink()->Compress(&compressor))
    379     return 1;
    380 #endif
    381   sink.WriteSnapshot();
    382   sink.WritePartialSnapshot();
    383 
    384   sink.WriteSpaceUsed(
    385       "context_",
    386       partial_ser.CurrentAllocationAddress(i::NEW_SPACE),
    387       partial_ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
    388       partial_ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
    389       partial_ser.CurrentAllocationAddress(i::CODE_SPACE),
    390       partial_ser.CurrentAllocationAddress(i::MAP_SPACE),
    391       partial_ser.CurrentAllocationAddress(i::CELL_SPACE),
    392       partial_ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
    393   sink.WriteSpaceUsed(
    394       "",
    395       ser.CurrentAllocationAddress(i::NEW_SPACE),
    396       ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
    397       ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
    398       ser.CurrentAllocationAddress(i::CODE_SPACE),
    399       ser.CurrentAllocationAddress(i::MAP_SPACE),
    400       ser.CurrentAllocationAddress(i::CELL_SPACE),
    401       ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
    402   return 0;
    403 }
    404