| OLD | NEW | 
|---|
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "src/profiler/sampling-heap-profiler.h" | 5 #include "src/profiler/sampling-heap-profiler.h" | 
| 6 | 6 | 
| 7 #include <stdint.h> | 7 #include <stdint.h> | 
| 8 #include <memory> | 8 #include <memory> | 
| 9 #include "src/api.h" | 9 #include "src/api.h" | 
| 10 #include "src/base/utils/random-number-generator.h" | 10 #include "src/base/utils/random-number-generator.h" | 
| 11 #include "src/frames-inl.h" | 11 #include "src/frames-inl.h" | 
| 12 #include "src/heap/heap.h" | 12 #include "src/heap/heap.h" | 
| 13 #include "src/isolate.h" | 13 #include "src/isolate.h" | 
| 14 #include "src/profiler/strings-storage.h" | 14 #include "src/profiler/strings-storage.h" | 
| 15 | 15 | 
| 16 namespace v8 { | 16 namespace v8 { | 
| 17 namespace internal { | 17 namespace internal { | 
| 18 | 18 | 
|  | 19 // We sample with a Poisson process, with constant average sampling interval. | 
|  | 20 // This follows the exponential probability distribution with parameter | 
|  | 21 // λ = 1/rate where rate is the average number of bytes between samples. | 
|  | 22 // | 
|  | 23 // Let u be a uniformly distributed random number between 0 and 1, then | 
|  | 24 // next_sample = (- ln u) / λ | 
|  | 25 intptr_t SamplingAllocationObserver::GetNextSampleInterval(uint64_t rate) { | 
|  | 26   if (FLAG_sampling_heap_profiler_suppress_randomness) { | 
|  | 27     return rate; | 
|  | 28   } | 
|  | 29   double u = random_->NextDouble(); | 
|  | 30   double next = (-std::log(u)) * rate; | 
|  | 31   return next < kPointerSize | 
|  | 32              ? kPointerSize | 
|  | 33              : (next > INT_MAX ? INT_MAX : static_cast<intptr_t>(next)); | 
|  | 34 } | 
|  | 35 | 
| 19 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names, | 36 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names, | 
| 20                                            uint64_t rate, int stack_depth) | 37                                            uint64_t rate, int stack_depth) | 
| 21     : InlineAllocationObserver(GetNextSampleInterval( | 38     : isolate_(heap->isolate()), | 
| 22           heap->isolate()->random_number_generator(), rate)), |  | 
| 23       isolate_(heap->isolate()), |  | 
| 24       heap_(heap), | 39       heap_(heap), | 
| 25       random_(isolate_->random_number_generator()), | 40       new_space_observer_(new SamplingAllocationObserver( | 
|  | 41           heap_, rate, rate, this, heap->isolate()->random_number_generator())), | 
|  | 42       other_spaces_observer_(new SamplingAllocationObserver( | 
|  | 43           heap_, rate, rate, this, heap->isolate()->random_number_generator())), | 
| 26       names_(names), | 44       names_(names), | 
| 27       samples_(), | 45       samples_(), | 
| 28       rate_(rate), |  | 
| 29       stack_depth_(stack_depth) { | 46       stack_depth_(stack_depth) { | 
| 30   heap->new_space()->AddInlineAllocationObserver(this); | 47   heap->new_space()->AddAllocationObserver(new_space_observer_.get()); | 
|  | 48   AllSpaces spaces(heap); | 
|  | 49   for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 
|  | 50     if (space != heap->new_space()) { | 
|  | 51       space->AddAllocationObserver(other_spaces_observer_.get()); | 
|  | 52     } | 
|  | 53   } | 
| 31 } | 54 } | 
| 32 | 55 | 
| 33 | 56 | 
| 34 SamplingHeapProfiler::~SamplingHeapProfiler() { | 57 SamplingHeapProfiler::~SamplingHeapProfiler() { | 
| 35   heap_->new_space()->RemoveInlineAllocationObserver(this); | 58   heap_->new_space()->RemoveAllocationObserver(new_space_observer_.get()); | 
|  | 59   AllSpaces spaces(heap_); | 
|  | 60   for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 
|  | 61     if (space != heap_->new_space()) { | 
|  | 62       space->RemoveAllocationObserver(other_spaces_observer_.get()); | 
|  | 63     } | 
|  | 64   } | 
| 36 | 65 | 
| 37   // Clear samples and drop all the weak references we are keeping. | 66   // Clear samples and drop all the weak references we are keeping. | 
| 38   std::set<SampledAllocation*>::iterator it; | 67   std::set<SampledAllocation*>::iterator it; | 
| 39   for (it = samples_.begin(); it != samples_.end(); ++it) { | 68   for (it = samples_.begin(); it != samples_.end(); ++it) { | 
| 40     delete *it; | 69     delete *it; | 
| 41   } | 70   } | 
| 42   std::set<SampledAllocation*> empty; | 71   std::set<SampledAllocation*> empty; | 
| 43   samples_.swap(empty); | 72   samples_.swap(empty); | 
| 44 } | 73 } | 
| 45 | 74 | 
| 46 void SamplingHeapProfiler::Step(int bytes_allocated, Address soon_object, |  | 
| 47                                 size_t size) { |  | 
| 48   DCHECK(heap_->gc_state() == Heap::NOT_IN_GC); |  | 
| 49   DCHECK(soon_object); |  | 
| 50   SampleObject(soon_object, size); |  | 
| 51 } |  | 
| 52 |  | 
| 53 | 75 | 
| 54 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { | 76 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { | 
| 55   DisallowHeapAllocation no_allocation; | 77   DisallowHeapAllocation no_allocation; | 
| 56 | 78 | 
| 57   HandleScope scope(isolate_); | 79   HandleScope scope(isolate_); | 
| 58   HeapObject* heap_object = HeapObject::FromAddress(soon_object); | 80   HeapObject* heap_object = HeapObject::FromAddress(soon_object); | 
| 59   Handle<Object> obj(heap_object, isolate_); | 81   Handle<Object> obj(heap_object, isolate_); | 
| 60 | 82 | 
| 61   // Mark the new block as FreeSpace to make sure the heap is iterable while we | 83   // Mark the new block as FreeSpace to make sure the heap is iterable while we | 
| 62   // are taking the sample. | 84   // are taking the sample. | 
| 63   heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); | 85   heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); | 
| 64 | 86 | 
| 65   Local<v8::Value> loc = v8::Utils::ToLocal(obj); | 87   Local<v8::Value> loc = v8::Utils::ToLocal(obj); | 
| 66 | 88 | 
| 67   SampledAllocation* sample = | 89   SampledAllocation* sample = | 
| 68       new SampledAllocation(this, isolate_, loc, size, stack_depth_); | 90       new SampledAllocation(this, isolate_, loc, size, stack_depth_); | 
| 69   samples_.insert(sample); | 91   samples_.insert(sample); | 
| 70 } | 92 } | 
| 71 | 93 | 
| 72 | 94 | 
| 73 // We sample with a Poisson process, with constant average sampling interval. |  | 
| 74 // This follows the exponential probability distribution with parameter |  | 
| 75 // λ = 1/rate where rate is the average number of bytes between samples. |  | 
| 76 // |  | 
| 77 // Let u be a uniformly distributed random number between 0 and 1, then |  | 
| 78 // next_sample = (- ln u) / λ |  | 
| 79 intptr_t SamplingHeapProfiler::GetNextSampleInterval( |  | 
| 80     base::RandomNumberGenerator* random, uint64_t rate) { |  | 
| 81   if (FLAG_sampling_heap_profiler_suppress_randomness) { |  | 
| 82     return rate; |  | 
| 83   } |  | 
| 84   double u = random->NextDouble(); |  | 
| 85   double next = (-std::log(u)) * rate; |  | 
| 86   return next < kPointerSize |  | 
| 87              ? kPointerSize |  | 
| 88              : (next > INT_MAX ? INT_MAX : static_cast<intptr_t>(next)); |  | 
| 89 } |  | 
| 90 |  | 
| 91 |  | 
| 92 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback( | 95 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback( | 
| 93     const WeakCallbackInfo<SampledAllocation>& data) { | 96     const WeakCallbackInfo<SampledAllocation>& data) { | 
| 94   SampledAllocation* sample = data.GetParameter(); | 97   SampledAllocation* sample = data.GetParameter(); | 
| 95   sample->sampling_heap_profiler_->samples_.erase(sample); | 98   sample->sampling_heap_profiler_->samples_.erase(sample); | 
| 96   delete sample; | 99   delete sample; | 
| 97 } | 100 } | 
| 98 | 101 | 
| 99 | 102 | 
| 100 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, | 103 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, | 
| 101                                                  StringsStorage* names) | 104                                                  StringsStorage* names) | 
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 152         name = "(IDLE)"; | 155         name = "(IDLE)"; | 
| 153         break; | 156         break; | 
| 154       case JS: | 157       case JS: | 
| 155         name = "(JS)"; | 158         name = "(JS)"; | 
| 156         break; | 159         break; | 
| 157     } | 160     } | 
| 158     stack_.push_back(new FunctionInfo(name)); | 161     stack_.push_back(new FunctionInfo(name)); | 
| 159   } | 162   } | 
| 160 } | 163 } | 
| 161 | 164 | 
| 162 | 165 v8::AllocationProfile::Node* SamplingHeapProfiler::AllocateNode( | 
| 163 SamplingHeapProfiler::Node* SamplingHeapProfiler::AllocateNode( |  | 
| 164     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 166     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 
| 165     FunctionInfo* function_info) { | 167     FunctionInfo* function_info) { | 
| 166   DCHECK(function_info->get_name()); | 168   DCHECK(function_info->get_name()); | 
| 167   DCHECK(function_info->get_script_name()); | 169   DCHECK(function_info->get_script_name()); | 
| 168 | 170 | 
| 169   int line = v8::AllocationProfile::kNoLineNumberInfo; | 171   int line = v8::AllocationProfile::kNoLineNumberInfo; | 
| 170   int column = v8::AllocationProfile::kNoColumnNumberInfo; | 172   int column = v8::AllocationProfile::kNoColumnNumberInfo; | 
| 171 | 173 | 
| 172   if (function_info->get_script_id() != v8::UnboundScript::kNoScriptId) { | 174   if (function_info->get_script_id() != v8::UnboundScript::kNoScriptId) { | 
| 173     // Cannot use std::map<T>::at because it is not available on android. | 175     // Cannot use std::map<T>::at because it is not available on android. | 
| 174     auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts); | 176     auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts); | 
| 175     Handle<Script> script(non_const_scripts[function_info->get_script_id()]); | 177     Handle<Script> script(non_const_scripts[function_info->get_script_id()]); | 
| 176 | 178 | 
| 177     line = | 179     line = | 
| 178         1 + Script::GetLineNumber(script, function_info->get_start_position()); | 180         1 + Script::GetLineNumber(script, function_info->get_start_position()); | 
| 179     column = 1 + Script::GetColumnNumber(script, | 181     column = 1 + Script::GetColumnNumber(script, | 
| 180                                          function_info->get_start_position()); | 182                                          function_info->get_start_position()); | 
| 181   } | 183   } | 
| 182 | 184 | 
| 183   profile->nodes().push_back( | 185   profile->nodes().push_back(v8::AllocationProfile::Node( | 
| 184       Node({ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | 186       {ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | 
| 185                 function_info->get_name())), | 187            function_info->get_name())), | 
| 186             ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | 188        ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | 
| 187                 function_info->get_script_name())), | 189            function_info->get_script_name())), | 
| 188             function_info->get_script_id(), function_info->get_start_position(), | 190        function_info->get_script_id(), function_info->get_start_position(), | 
| 189             line, column, std::vector<Node*>(), | 191        line, column, std::vector<v8::AllocationProfile::Node*>(), | 
| 190             std::vector<v8::AllocationProfile::Allocation>()})); | 192        std::vector<v8::AllocationProfile::Allocation>()})); | 
| 191 | 193 | 
| 192   return &profile->nodes().back(); | 194   return &profile->nodes().back(); | 
| 193 } | 195 } | 
| 194 | 196 | 
| 195 | 197 v8::AllocationProfile::Node* SamplingHeapProfiler::FindOrAddChildNode( | 
| 196 SamplingHeapProfiler::Node* SamplingHeapProfiler::FindOrAddChildNode( |  | 
| 197     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 198     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 
| 198     Node* parent, FunctionInfo* function_info) { | 199     v8::AllocationProfile::Node* parent, FunctionInfo* function_info) { | 
| 199   for (Node* child : parent->children) { | 200   for (v8::AllocationProfile::Node* child : parent->children) { | 
| 200     if (child->script_id == function_info->get_script_id() && | 201     if (child->script_id == function_info->get_script_id() && | 
| 201         child->start_position == function_info->get_start_position()) | 202         child->start_position == function_info->get_start_position()) | 
| 202       return child; | 203       return child; | 
| 203   } | 204   } | 
| 204   Node* child = AllocateNode(profile, scripts, function_info); | 205   v8::AllocationProfile::Node* child = | 
|  | 206       AllocateNode(profile, scripts, function_info); | 
| 205   parent->children.push_back(child); | 207   parent->children.push_back(child); | 
| 206   return child; | 208   return child; | 
| 207 } | 209 } | 
| 208 | 210 | 
| 209 | 211 v8::AllocationProfile::Node* SamplingHeapProfiler::AddStack( | 
| 210 SamplingHeapProfiler::Node* SamplingHeapProfiler::AddStack( |  | 
| 211     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 212     AllocationProfile* profile, const std::map<int, Script*>& scripts, | 
| 212     const std::vector<FunctionInfo*>& stack) { | 213     const std::vector<FunctionInfo*>& stack) { | 
| 213   Node* node = profile->GetRootNode(); | 214   v8::AllocationProfile::Node* node = profile->GetRootNode(); | 
| 214 | 215 | 
| 215   // We need to process the stack in reverse order as the top of the stack is | 216   // We need to process the stack in reverse order as the top of the stack is | 
| 216   // the first element in the list. | 217   // the first element in the list. | 
| 217   for (auto it = stack.rbegin(); it != stack.rend(); ++it) { | 218   for (auto it = stack.rbegin(); it != stack.rend(); ++it) { | 
| 218     FunctionInfo* function_info = *it; | 219     FunctionInfo* function_info = *it; | 
| 219     node = FindOrAddChildNode(profile, scripts, node, function_info); | 220     node = FindOrAddChildNode(profile, scripts, node, function_info); | 
| 220   } | 221   } | 
| 221   return node; | 222   return node; | 
| 222 } | 223 } | 
| 223 | 224 | 
| (...skipping 10 matching lines...) Expand all  Loading... | 
| 234     } | 235     } | 
| 235   } | 236   } | 
| 236 | 237 | 
| 237   auto profile = new v8::internal::AllocationProfile(); | 238   auto profile = new v8::internal::AllocationProfile(); | 
| 238 | 239 | 
| 239   // Create the root node. | 240   // Create the root node. | 
| 240   FunctionInfo function_info("(root)"); | 241   FunctionInfo function_info("(root)"); | 
| 241   AllocateNode(profile, scripts, &function_info); | 242   AllocateNode(profile, scripts, &function_info); | 
| 242 | 243 | 
| 243   for (SampledAllocation* allocation : samples_) { | 244   for (SampledAllocation* allocation : samples_) { | 
| 244     Node* node = AddStack(profile, scripts, allocation->get_stack()); | 245     v8::AllocationProfile::Node* node = | 
|  | 246         AddStack(profile, scripts, allocation->get_stack()); | 
| 245     node->allocations.push_back({allocation->get_size(), 1}); | 247     node->allocations.push_back({allocation->get_size(), 1}); | 
| 246   } | 248   } | 
| 247 | 249 | 
| 248   return profile; | 250   return profile; | 
| 249 } | 251 } | 
| 250 | 252 | 
| 251 | 253 | 
| 252 }  // namespace internal | 254 }  // namespace internal | 
| 253 }  // namespace v8 | 255 }  // namespace v8 | 
| OLD | NEW | 
|---|