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 static const unsigned int kMaxCounters = 256;
     47 
     48 // A single counter in a counter collection.
     49 class Counter {
     50  public:
     51   static const int kMaxNameSize = 64;
     52   int32_t* Bind(const char* name) {
     53     int i;
     54     for (i = 0; i < kMaxNameSize - 1 && name[i]; i++) {
     55       name_[i] = name[i];
     56     }
     57     name_[i] = '\0';
     58     return &counter_;
     59   }
     60  private:
     61   int32_t counter_;
     62   uint8_t name_[kMaxNameSize];
     63 };
     64 
     65 
     66 // A set of counters and associated information.  An instance of this
     67 // class is stored directly in the memory-mapped counters file if
     68 // the --save-counters options is used
     69 class CounterCollection {
     70  public:
     71   CounterCollection() {
     72     magic_number_ = 0xDEADFACE;
     73     max_counters_ = kMaxCounters;
     74     max_name_size_ = Counter::kMaxNameSize;
     75     counters_in_use_ = 0;
     76   }
     77   Counter* GetNextCounter() {
     78     if (counters_in_use_ == kMaxCounters) return NULL;
     79     return &counters_[counters_in_use_++];
     80   }
     81  private:
     82   uint32_t magic_number_;
     83   uint32_t max_counters_;
     84   uint32_t max_name_size_;
     85   uint32_t counters_in_use_;
     86   Counter counters_[kMaxCounters];
     87 };
     88 
     89 
     90 class Compressor {
     91  public:
     92   virtual ~Compressor() {}
     93   virtual bool Compress(i::Vector<char> input) = 0;
     94   virtual i::Vector<char>* output() = 0;
     95 };
     96 
     97 
     98 class PartialSnapshotSink : public i::SnapshotByteSink {
     99  public:
    100   PartialSnapshotSink() : data_(), raw_size_(-1) { }
    101   virtual ~PartialSnapshotSink() { data_.Free(); }
    102   virtual void Put(int byte, const char* description) {
    103     data_.Add(byte);
    104   }
    105   virtual int Position() { return data_.length(); }
    106   void Print(FILE* fp) {
    107     int length = Position();
    108     for (int j = 0; j < length; j++) {
    109       if ((j & 0x1f) == 0x1f) {
    110         fprintf(fp, "\n");
    111       }
    112       if (j != 0) {
    113         fprintf(fp, ",");
    114       }
    115       fprintf(fp, "%u", static_cast<unsigned char>(at(j)));
    116     }
    117   }
    118   char at(int i) { return data_[i]; }
    119   bool Compress(Compressor* compressor) {
    120     ASSERT_EQ(-1, raw_size_);
    121     raw_size_ = data_.length();
    122     if (!compressor->Compress(data_.ToVector())) return false;
    123     data_.Clear();
    124     data_.AddAll(*compressor->output());
    125     return true;
    126   }
    127   int raw_size() { return raw_size_; }
    128 
    129  private:
    130   i::List<char> data_;
    131   int raw_size_;
    132 };
    133 
    134 
    135 class CppByteSink : public PartialSnapshotSink {
    136  public:
    137   explicit CppByteSink(const char* snapshot_file) {
    138     fp_ = i::OS::FOpen(snapshot_file, "wb");
    139     if (fp_ == NULL) {
    140       i::PrintF("Unable to write to snapshot file \"%s\"\n", snapshot_file);
    141       exit(1);
    142     }
    143     fprintf(fp_, "// Autogenerated snapshot file. Do not edit.\n\n");
    144     fprintf(fp_, "#include \"v8.h\"\n");
    145     fprintf(fp_, "#include \"platform.h\"\n\n");
    146     fprintf(fp_, "#include \"snapshot.h\"\n\n");
    147     fprintf(fp_, "namespace v8 {\nnamespace internal {\n\n");
    148     fprintf(fp_, "const byte Snapshot::data_[] = {");
    149   }
    150 
    151   virtual ~CppByteSink() {
    152     fprintf(fp_, "const int Snapshot::size_ = %d;\n", Position());
    153 #ifdef COMPRESS_STARTUP_DATA_BZ2
    154     fprintf(fp_, "const byte* Snapshot::raw_data_ = NULL;\n");
    155     fprintf(fp_,
    156             "const int Snapshot::raw_size_ = %d;\n\n",
    157             raw_size());
    158 #else
    159     fprintf(fp_,
    160             "const byte* Snapshot::raw_data_ = Snapshot::data_;\n");
    161     fprintf(fp_,
    162             "const int Snapshot::raw_size_ = Snapshot::size_;\n\n");
    163 #endif
    164     fprintf(fp_, "} }  // namespace v8::internal\n");
    165     fclose(fp_);
    166   }
    167 
    168   void WriteSpaceUsed(
    169       const char* prefix,
    170       int new_space_used,
    171       int pointer_space_used,
    172       int data_space_used,
    173       int code_space_used,
    174       int map_space_used,
    175       int cell_space_used,
    176       int property_cell_space_used) {
    177     fprintf(fp_,
    178             "const int Snapshot::%snew_space_used_ = %d;\n",
    179             prefix,
    180             new_space_used);
    181     fprintf(fp_,
    182             "const int Snapshot::%spointer_space_used_ = %d;\n",
    183             prefix,
    184             pointer_space_used);
    185     fprintf(fp_,
    186             "const int Snapshot::%sdata_space_used_ = %d;\n",
    187             prefix,
    188             data_space_used);
    189     fprintf(fp_,
    190             "const int Snapshot::%scode_space_used_ = %d;\n",
    191             prefix,
    192             code_space_used);
    193     fprintf(fp_,
    194             "const int Snapshot::%smap_space_used_ = %d;\n",
    195             prefix,
    196             map_space_used);
    197     fprintf(fp_,
    198             "const int Snapshot::%scell_space_used_ = %d;\n",
    199             prefix,
    200             cell_space_used);
    201     fprintf(fp_,
    202             "const int Snapshot::%sproperty_cell_space_used_ = %d;\n",
    203             prefix,
    204             property_cell_space_used);
    205   }
    206 
    207   void WritePartialSnapshot() {
    208     int length = partial_sink_.Position();
    209     fprintf(fp_, "};\n\n");
    210     fprintf(fp_, "const int Snapshot::context_size_ = %d;\n",  length);
    211 #ifdef COMPRESS_STARTUP_DATA_BZ2
    212     fprintf(fp_,
    213             "const int Snapshot::context_raw_size_ = %d;\n",
    214             partial_sink_.raw_size());
    215 #else
    216     fprintf(fp_,
    217             "const int Snapshot::context_raw_size_ = "
    218             "Snapshot::context_size_;\n");
    219 #endif
    220     fprintf(fp_, "const byte Snapshot::context_data_[] = {\n");
    221     partial_sink_.Print(fp_);
    222     fprintf(fp_, "};\n\n");
    223 #ifdef COMPRESS_STARTUP_DATA_BZ2
    224     fprintf(fp_, "const byte* Snapshot::context_raw_data_ = NULL;\n");
    225 #else
    226     fprintf(fp_, "const byte* Snapshot::context_raw_data_ ="
    227             " Snapshot::context_data_;\n");
    228 #endif
    229   }
    230 
    231   void WriteSnapshot() {
    232     Print(fp_);
    233   }
    234 
    235   PartialSnapshotSink* partial_sink() { return &partial_sink_; }
    236 
    237  private:
    238   FILE* fp_;
    239   PartialSnapshotSink partial_sink_;
    240 };
    241 
    242 
    243 #ifdef COMPRESS_STARTUP_DATA_BZ2
    244 class BZip2Compressor : public Compressor {
    245  public:
    246   BZip2Compressor() : output_(NULL) {}
    247   virtual ~BZip2Compressor() {
    248     delete output_;
    249   }
    250   virtual bool Compress(i::Vector<char> input) {
    251     delete output_;
    252     output_ = new i::ScopedVector<char>((input.length() * 101) / 100 + 1000);
    253     unsigned int output_length_ = output_->length();
    254     int result = BZ2_bzBuffToBuffCompress(output_->start(), &output_length_,
    255                                           input.start(), input.length(),
    256                                           9, 1, 0);
    257     if (result == BZ_OK) {
    258       output_->Truncate(output_length_);
    259       return true;
    260     } else {
    261       fprintf(stderr, "bzlib error code: %d\n", result);
    262       return false;
    263     }
    264   }
    265   virtual i::Vector<char>* output() { return output_; }
    266 
    267  private:
    268   i::ScopedVector<char>* output_;
    269 };
    270 
    271 
    272 class BZip2Decompressor : public StartupDataDecompressor {
    273  public:
    274   virtual ~BZip2Decompressor() { }
    275 
    276  protected:
    277   virtual int DecompressData(char* raw_data,
    278                              int* raw_data_size,
    279                              const char* compressed_data,
    280                              int compressed_data_size) {
    281     ASSERT_EQ(StartupData::kBZip2,
    282               V8::GetCompressedStartupDataAlgorithm());
    283     unsigned int decompressed_size = *raw_data_size;
    284     int result =
    285         BZ2_bzBuffToBuffDecompress(raw_data,
    286                                    &decompressed_size,
    287                                    const_cast<char*>(compressed_data),
    288                                    compressed_data_size,
    289                                    0, 1);
    290     if (result == BZ_OK) {
    291       *raw_data_size = decompressed_size;
    292     }
    293     return result;
    294   }
    295 };
    296 #endif
    297 
    298 
    299 void DumpException(Handle<Message> message) {
    300   String::Utf8Value message_string(message->Get());
    301   String::Utf8Value message_line(message->GetSourceLine());
    302   fprintf(stderr, "%s at line %d\n", *message_string, message->GetLineNumber());
    303   fprintf(stderr, "%s\n", *message_line);
    304   for (int i = 0; i <= message->GetEndColumn(); ++i) {
    305     fprintf(stderr, "%c", i < message->GetStartColumn() ? ' ' : '^');
    306   }
    307   fprintf(stderr, "\n");
    308 }
    309 
    310 
    311 int main(int argc, char** argv) {
    312   V8::InitializeICU();
    313 
    314   // By default, log code create information in the snapshot.
    315   i::FLAG_log_code = true;
    316 
    317   // Disable the i18n extension, as it doesn't support being snapshotted yet.
    318   i::FLAG_enable_i18n = false;
    319 
    320   // Print the usage if an error occurs when parsing the command line
    321   // flags or if the help flag is set.
    322   int result = i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
    323   if (result > 0 || argc != 2 || i::FLAG_help) {
    324     ::printf("Usage: %s [flag] ... outfile\n", argv[0]);
    325     i::FlagList::PrintHelp();
    326     return !i::FLAG_help;
    327   }
    328 #ifdef COMPRESS_STARTUP_DATA_BZ2
    329   BZip2Decompressor natives_decompressor;
    330   int bz2_result = natives_decompressor.Decompress();
    331   if (bz2_result != BZ_OK) {
    332     fprintf(stderr, "bzip error code: %d\n", bz2_result);
    333     exit(1);
    334   }
    335 #endif
    336   i::Serializer::Enable();
    337   Isolate* isolate = Isolate::GetCurrent();
    338   Persistent<Context> context;
    339   {
    340     HandleScope handle_scope(isolate);
    341     context.Reset(isolate, Context::New(isolate));
    342   }
    343 
    344   if (context.IsEmpty()) {
    345     fprintf(stderr,
    346             "\nException thrown while compiling natives - see above.\n\n");
    347     exit(1);
    348   }
    349   if (i::FLAG_extra_code != NULL) {
    350     // Capture 100 frames if anything happens.
    351     V8::SetCaptureStackTraceForUncaughtExceptions(true, 100);
    352     HandleScope scope(isolate);
    353     v8::Context::Scope(v8::Local<v8::Context>::New(isolate, context));
    354     const char* name = i::FLAG_extra_code;
    355     FILE* file = i::OS::FOpen(name, "rb");
    356     if (file == NULL) {
    357       fprintf(stderr, "Failed to open '%s': errno %d\n", name, errno);
    358       exit(1);
    359     }
    360 
    361     fseek(file, 0, SEEK_END);
    362     int size = ftell(file);
    363     rewind(file);
    364 
    365     char* chars = new char[size + 1];
    366     chars[size] = '\0';
    367     for (int i = 0; i < size;) {
    368       int read = static_cast<int>(fread(&chars[i], 1, size - i, file));
    369       if (read < 0) {
    370         fprintf(stderr, "Failed to read '%s': errno %d\n", name, errno);
    371         exit(1);
    372       }
    373       i += read;
    374     }
    375     fclose(file);
    376     Local<String> source = String::New(chars);
    377     TryCatch try_catch;
    378     Local<Script> script = Script::Compile(source);
    379     if (try_catch.HasCaught()) {
    380       fprintf(stderr, "Failure compiling '%s'\n", name);
    381       DumpException(try_catch.Message());
    382       exit(1);
    383     }
    384     script->Run();
    385     if (try_catch.HasCaught()) {
    386       fprintf(stderr, "Failure running '%s'\n", name);
    387       DumpException(try_catch.Message());
    388       exit(1);
    389     }
    390   }
    391   // Make sure all builtin scripts are cached.
    392   { HandleScope scope(isolate);
    393     for (int i = 0; i < i::Natives::GetBuiltinsCount(); i++) {
    394       i::Isolate::Current()->bootstrapper()->NativesSourceLookup(i);
    395     }
    396   }
    397   // If we don't do this then we end up with a stray root pointing at the
    398   // context even after we have disposed of the context.
    399   HEAP->CollectAllGarbage(i::Heap::kNoGCFlags, "mksnapshot");
    400   i::Object* raw_context = *v8::Utils::OpenPersistent(context);
    401   context.Dispose(isolate);
    402   CppByteSink sink(argv[1]);
    403   // This results in a somewhat smaller snapshot, probably because it gets rid
    404   // of some things that are cached between garbage collections.
    405   i::StartupSerializer ser(&sink);
    406   ser.SerializeStrongReferences();
    407 
    408   i::PartialSerializer partial_ser(&ser, sink.partial_sink());
    409   partial_ser.Serialize(&raw_context);
    410 
    411   ser.SerializeWeakReferences();
    412 
    413 #ifdef COMPRESS_STARTUP_DATA_BZ2
    414   BZip2Compressor compressor;
    415   if (!sink.Compress(&compressor))
    416     return 1;
    417   if (!sink.partial_sink()->Compress(&compressor))
    418     return 1;
    419 #endif
    420   sink.WriteSnapshot();
    421   sink.WritePartialSnapshot();
    422 
    423   sink.WriteSpaceUsed(
    424       "context_",
    425       partial_ser.CurrentAllocationAddress(i::NEW_SPACE),
    426       partial_ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
    427       partial_ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
    428       partial_ser.CurrentAllocationAddress(i::CODE_SPACE),
    429       partial_ser.CurrentAllocationAddress(i::MAP_SPACE),
    430       partial_ser.CurrentAllocationAddress(i::CELL_SPACE),
    431       partial_ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
    432   sink.WriteSpaceUsed(
    433       "",
    434       ser.CurrentAllocationAddress(i::NEW_SPACE),
    435       ser.CurrentAllocationAddress(i::OLD_POINTER_SPACE),
    436       ser.CurrentAllocationAddress(i::OLD_DATA_SPACE),
    437       ser.CurrentAllocationAddress(i::CODE_SPACE),
    438       ser.CurrentAllocationAddress(i::MAP_SPACE),
    439       ser.CurrentAllocationAddress(i::CELL_SPACE),
    440       ser.CurrentAllocationAddress(i::PROPERTY_CELL_SPACE));
    441   return 0;
    442 }
    443