OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "src/api.h" |
| 6 #include "src/frames-inl.h" |
| 7 #include "src/heap/heap.h" |
| 8 #include "src/isolate.h" |
| 9 #include "src/profiler/sampling-heap-profiler.h" |
| 10 #include "src/profiler/strings-storage.h" |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 |
| 15 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names, |
| 16 uint64_t rate, int stack_depth) |
| 17 : InlineAllocationObserver(GetNextSampleInterval( |
| 18 heap->isolate()->random_number_generator(), rate)), |
| 19 isolate_(heap->isolate()), |
| 20 heap_(heap), |
| 21 random_(isolate_->random_number_generator()), |
| 22 names_(names), |
| 23 samples_(), |
| 24 rate_(rate), |
| 25 stack_depth_(stack_depth) { |
| 26 heap->new_space()->AddInlineAllocationObserver(this); |
| 27 } |
| 28 |
| 29 |
| 30 SamplingHeapProfiler::~SamplingHeapProfiler() { |
| 31 heap_->new_space()->RemoveInlineAllocationObserver(this); |
| 32 |
| 33 // Clear samples and drop all the weak references we are keeping. |
| 34 std::set<SampledAllocation*>::iterator it; |
| 35 for (it = samples_.begin(); it != samples_.end(); ++it) { |
| 36 delete *it; |
| 37 } |
| 38 std::set<SampledAllocation*> empty{}; |
| 39 samples_.swap(empty); |
| 40 } |
| 41 |
| 42 void SamplingHeapProfiler::Step(int bytes_allocated, Address soon_object, |
| 43 size_t size) { |
| 44 DCHECK(heap_->gc_state() == Heap::NOT_IN_GC); |
| 45 DCHECK(soon_object); |
| 46 SampleObject(soon_object, size); |
| 47 } |
| 48 |
| 49 |
| 50 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { |
| 51 DisallowHeapAllocation no_allocation; |
| 52 |
| 53 HandleScope scope(isolate_); |
| 54 HeapObject* heap_object = HeapObject::FromAddress(soon_object); |
| 55 Handle<Object> obj(heap_object, isolate_); |
| 56 |
| 57 // Mark the new block as FreeSpace to make sure the heap is iterable while we |
| 58 // are taking the sample. |
| 59 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); |
| 60 |
| 61 Local<v8::Value> loc = v8::Utils::ToLocal(obj); |
| 62 |
| 63 SampledAllocation* sample = |
| 64 new SampledAllocation(this, isolate_, loc, size, stack_depth_); |
| 65 samples_.insert(sample); |
| 66 } |
| 67 |
| 68 |
| 69 // We sample with a Poisson process, with constant average sampling interval. |
| 70 // This follows the exponential probability distribution with parameter |
| 71 // λ = 1/rate where rate is the average number of bytes between samples. |
| 72 // |
| 73 // Let u be a uniformly distributed random number between 0 and 1, then |
| 74 // next_sample = (- ln u) / λ |
| 75 intptr_t SamplingHeapProfiler::GetNextSampleInterval( |
| 76 base::RandomNumberGenerator* random, uint64_t rate) { |
| 77 double u = random->NextDouble(); |
| 78 double next = (-std::log(u)) * rate; |
| 79 return std::max(static_cast<intptr_t>(kPointerSize), |
| 80 static_cast<intptr_t>(next)); |
| 81 } |
| 82 |
| 83 |
| 84 void SamplingHeapProfiler::GetHeapSample(OutputStream* stream) { |
| 85 std::map<int, Script*> scripts; |
| 86 { |
| 87 Script::Iterator iterator(isolate_); |
| 88 Script* script; |
| 89 while ((script = iterator.Next())) { |
| 90 scripts[script->id()] = script; |
| 91 } |
| 92 } |
| 93 |
| 94 OutputStreamWriter writer(stream); |
| 95 writer.AddString("[\n"); |
| 96 std::set<SampledAllocation*>::iterator it; |
| 97 for (it = samples_.begin(); it != samples_.end(); ++it) { |
| 98 auto sample = *it; |
| 99 if (it != samples_.begin()) { |
| 100 writer.AddString(","); |
| 101 } |
| 102 writer.AddString(" {\"size\": "); |
| 103 writer.AddNumber(static_cast<unsigned>(sample->get_size())); |
| 104 writer.AddString(", \"stack\": [\n"); |
| 105 List<FunctionInfo*>& stack = sample->get_stack(); |
| 106 for (int i = 0; i < stack.length(); ++i) { |
| 107 FunctionInfo* info = stack[i]; |
| 108 int line = -1; |
| 109 int column = -1; |
| 110 if (info->script_id != -1) { |
| 111 Handle<Script> script(scripts[info->script_id]); |
| 112 line = Script::GetLineNumber(script, info->start_position); |
| 113 column = Script::GetColumnNumber(script, info->start_position); |
| 114 } |
| 115 |
| 116 writer.AddString("\t{\"name\": \""); |
| 117 writer.AddString(info->name); |
| 118 writer.AddString("\""); |
| 119 if (line != -1) { |
| 120 DCHECK(column != -1); |
| 121 writer.AddString(", \"line\": "); |
| 122 writer.AddNumber(line); |
| 123 writer.AddString(", \"column\": "); |
| 124 writer.AddNumber(column); |
| 125 } |
| 126 writer.AddString(", \"scriptName\": \""); |
| 127 writer.AddString(info->script_name); |
| 128 if (i < (stack.length() - 1)) { |
| 129 writer.AddString("\"},\n"); |
| 130 } else { |
| 131 writer.AddString("\"}\n"); |
| 132 } |
| 133 } |
| 134 writer.AddString(" ]}\n"); |
| 135 } |
| 136 writer.AddString("]\n"); |
| 137 writer.Finalize(); |
| 138 } |
| 139 |
| 140 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback( |
| 141 const WeakCallbackInfo<SampledAllocation>& data) { |
| 142 SampledAllocation* sample = data.GetParameter(); |
| 143 sample->shp_->samples_.erase(sample); |
| 144 delete sample; |
| 145 } |
| 146 |
| 147 |
| 148 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, |
| 149 StringsStorage* names) |
| 150 : name(names->GetFunctionName(shared->DebugName())), |
| 151 script_name(""), |
| 152 script_id(-1), |
| 153 start_position(-1) { |
| 154 if (shared->script()->IsScript()) { |
| 155 Script* script = Script::cast(shared->script()); |
| 156 script_id = script->id(); |
| 157 start_position = shared->start_position(); |
| 158 if (script->name()->IsName()) { |
| 159 Name* name = Name::cast(script->name()); |
| 160 script_name = names->GetName(name); |
| 161 } |
| 162 } |
| 163 } |
| 164 |
| 165 SamplingHeapProfiler::SampledAllocation::SampledAllocation( |
| 166 SamplingHeapProfiler* shp, Isolate* isolate, Local<Value> local, |
| 167 size_t size, int max_frames) |
| 168 : shp_(shp), |
| 169 global_(reinterpret_cast<v8::Isolate*>(isolate), local), |
| 170 size_(size) { |
| 171 global_.SetWeak(this, OnWeakCallback, WeakCallbackType::kParameter); |
| 172 |
| 173 |
| 174 StackTraceFrameIterator it(isolate); |
| 175 int frames_captured = 0; |
| 176 while (!it.done() && frames_captured < max_frames) { |
| 177 JavaScriptFrame* frame = it.frame(); |
| 178 SharedFunctionInfo* shared = frame->function()->shared(); |
| 179 stack_.Add(new FunctionInfo(shared, shp->names())); |
| 180 |
| 181 frames_captured++; |
| 182 it.Advance(); |
| 183 } |
| 184 } |
| 185 |
| 186 |
| 187 } // namespace internal |
| 188 } // namespace v8 |
OLD | NEW |