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
44using namespace v8;
45
46
47class 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
55class 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
92class 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
201class 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
229class 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
256void 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
268int 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