Home | History | Annotate | Download | only in v8
      1 /*
      2  * Copyright (c) 2011, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "bindings/v8/ScriptProfiler.h"
     33 
     34 #include "bindings/core/v8/V8Node.h"
     35 #include "bindings/core/v8/V8Window.h"
     36 #include "bindings/v8/RetainedDOMInfo.h"
     37 #include "bindings/v8/ScriptValue.h"
     38 #include "bindings/v8/V8Binding.h"
     39 #include "bindings/v8/WrapperTypeInfo.h"
     40 #include "core/dom/Document.h"
     41 #include "core/inspector/BindingVisitors.h"
     42 
     43 #include <v8-profiler.h>
     44 #include <v8.h>
     45 
     46 #include "wtf/ThreadSpecific.h"
     47 
     48 namespace WebCore {
     49 
     50 typedef HashMap<String, double> ProfileNameIdleTimeMap;
     51 
     52 void ScriptProfiler::setSamplingInterval(int intervalUs)
     53 {
     54     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     55     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     56     if (profiler)
     57         profiler->SetSamplingInterval(intervalUs);
     58 }
     59 
     60 void ScriptProfiler::start(const String& title)
     61 {
     62     ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
     63     if (profileNameIdleTimeMap->contains(title))
     64         return;
     65     profileNameIdleTimeMap->add(title, 0);
     66 
     67     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     68     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     69     if (!profiler)
     70         return;
     71     v8::HandleScope handleScope(isolate);
     72     profiler->StartProfiling(v8String(isolate, title), true);
     73 }
     74 
     75 PassRefPtrWillBeRawPtr<ScriptProfile> ScriptProfiler::stop(const String& title)
     76 {
     77     v8::Isolate* isolate = v8::Isolate::GetCurrent();
     78     v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
     79     if (!profiler)
     80         return nullptr;
     81     v8::HandleScope handleScope(isolate);
     82     v8::CpuProfile* profile = profiler->StopProfiling(v8String(isolate, title));
     83     if (!profile)
     84         return nullptr;
     85 
     86     String profileTitle = toCoreString(profile->GetTitle());
     87     double idleTime = 0.0;
     88     ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
     89     ProfileNameIdleTimeMap::iterator profileIdleTime = profileNameIdleTimeMap->find(profileTitle);
     90     if (profileIdleTime != profileNameIdleTimeMap->end()) {
     91         idleTime = profileIdleTime->value * 1000.0;
     92         profileNameIdleTimeMap->remove(profileIdleTime);
     93     }
     94 
     95     return ScriptProfile::create(profile, idleTime);
     96 }
     97 
     98 void ScriptProfiler::collectGarbage()
     99 {
    100     v8::V8::LowMemoryNotification();
    101 }
    102 
    103 ScriptValue ScriptProfiler::objectByHeapObjectId(unsigned id)
    104 {
    105     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    106     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    107     v8::HandleScope handleScope(isolate);
    108     v8::Handle<v8::Value> value = profiler->FindObjectById(id);
    109     if (value.IsEmpty() || !value->IsObject())
    110         return ScriptValue();
    111 
    112     v8::Handle<v8::Object> object = value.As<v8::Object>();
    113 
    114     if (object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount) {
    115         v8::Handle<v8::Value> wrapper = object->GetInternalField(v8DOMWrapperObjectIndex);
    116         // Skip wrapper boilerplates which are like regular wrappers but don't have
    117         // native object.
    118         if (!wrapper.IsEmpty() && wrapper->IsUndefined())
    119             return ScriptValue();
    120     }
    121 
    122     ScriptState* scriptState = ScriptState::from(object->CreationContext());
    123     return ScriptValue(scriptState, object);
    124 }
    125 
    126 unsigned ScriptProfiler::getHeapObjectId(const ScriptValue& value)
    127 {
    128     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    129     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    130     v8::SnapshotObjectId id = profiler->GetObjectId(value.v8Value());
    131     return id;
    132 }
    133 
    134 void ScriptProfiler::clearHeapObjectIds()
    135 {
    136     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    137     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    138     profiler->ClearObjectIds();
    139 }
    140 
    141 namespace {
    142 
    143 class ActivityControlAdapter FINAL : public v8::ActivityControl {
    144 public:
    145     ActivityControlAdapter(ScriptProfiler::HeapSnapshotProgress* progress)
    146             : m_progress(progress), m_firstReport(true) { }
    147     virtual ControlOption ReportProgressValue(int done, int total) OVERRIDE
    148     {
    149         ControlOption result = m_progress->isCanceled() ? kAbort : kContinue;
    150         if (m_firstReport) {
    151             m_firstReport = false;
    152             m_progress->Start(total);
    153         } else
    154             m_progress->Worked(done);
    155         if (done >= total)
    156             m_progress->Done();
    157         return result;
    158     }
    159 private:
    160     ScriptProfiler::HeapSnapshotProgress* m_progress;
    161     bool m_firstReport;
    162 };
    163 
    164 class GlobalObjectNameResolver FINAL : public v8::HeapProfiler::ObjectNameResolver {
    165 public:
    166     virtual const char* GetName(v8::Handle<v8::Object> object) OVERRIDE
    167     {
    168         LocalDOMWindow* window = toDOMWindow(object, v8::Isolate::GetCurrent());
    169         if (!window)
    170             return 0;
    171         CString url = window->document()->url().string().utf8();
    172         m_strings.append(url);
    173         return url.data();
    174     }
    175 
    176 private:
    177     Vector<CString> m_strings;
    178 };
    179 
    180 } // namespace
    181 
    182 void ScriptProfiler::startTrackingHeapObjects(bool trackAllocations)
    183 {
    184     v8::Isolate::GetCurrent()->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
    185 }
    186 
    187 namespace {
    188 
    189 class HeapStatsStream : public v8::OutputStream {
    190 public:
    191     HeapStatsStream(ScriptProfiler::OutputStream* stream) : m_stream(stream) { }
    192     virtual void EndOfStream() OVERRIDE { }
    193 
    194     virtual WriteResult WriteAsciiChunk(char* data, int size) OVERRIDE
    195     {
    196         ASSERT(false);
    197         return kAbort;
    198     }
    199 
    200     virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count) OVERRIDE
    201     {
    202         Vector<uint32_t> rawData(count * 3);
    203         for (int i = 0; i < count; ++i) {
    204             int offset = i * 3;
    205             rawData[offset] = updateData[i].index;
    206             rawData[offset + 1] = updateData[i].count;
    207             rawData[offset + 2] = updateData[i].size;
    208         }
    209         m_stream->write(rawData.data(), rawData.size());
    210         return kContinue;
    211     }
    212 
    213 private:
    214     ScriptProfiler::OutputStream* m_stream;
    215 };
    216 
    217 }
    218 
    219 unsigned ScriptProfiler::requestHeapStatsUpdate(ScriptProfiler::OutputStream* stream)
    220 {
    221     HeapStatsStream heapStatsStream(stream);
    222     return v8::Isolate::GetCurrent()->GetHeapProfiler()->GetHeapStats(&heapStatsStream);
    223 }
    224 
    225 void ScriptProfiler::stopTrackingHeapObjects()
    226 {
    227     v8::Isolate::GetCurrent()->GetHeapProfiler()->StopTrackingHeapObjects();
    228 }
    229 
    230 // FIXME: This method should receive a ScriptState, from which we should retrieve an Isolate.
    231 PassRefPtr<ScriptHeapSnapshot> ScriptProfiler::takeHeapSnapshot(const String& title, HeapSnapshotProgress* control)
    232 {
    233     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    234     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    235     if (!profiler)
    236         return nullptr;
    237     v8::HandleScope handleScope(isolate);
    238     ASSERT(control);
    239     ActivityControlAdapter adapter(control);
    240     GlobalObjectNameResolver resolver;
    241     const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(v8String(isolate, title), &adapter, &resolver);
    242     return snapshot ? ScriptHeapSnapshot::create(snapshot) : nullptr;
    243 }
    244 
    245 static v8::RetainedObjectInfo* retainedDOMInfo(uint16_t classId, v8::Handle<v8::Value> wrapper)
    246 {
    247     ASSERT(classId == v8DOMNodeClassId);
    248     if (!wrapper->IsObject())
    249         return 0;
    250     Node* node = V8Node::toNative(wrapper.As<v8::Object>());
    251     return node ? new RetainedDOMInfo(node) : 0;
    252 }
    253 
    254 void ScriptProfiler::initialize()
    255 {
    256     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    257     v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    258     if (profiler)
    259         profiler->SetWrapperClassInfoProvider(v8DOMNodeClassId, &retainedDOMInfo);
    260 }
    261 
    262 void ScriptProfiler::visitNodeWrappers(WrappedNodeVisitor* visitor)
    263 {
    264     // visitNodeWrappers() should receive a ScriptState and retrieve an Isolate
    265     // from the ScriptState.
    266     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    267     v8::HandleScope handleScope(isolate);
    268 
    269     class DOMNodeWrapperVisitor : public v8::PersistentHandleVisitor {
    270     public:
    271         DOMNodeWrapperVisitor(WrappedNodeVisitor* visitor, v8::Isolate* isolate)
    272             : m_visitor(visitor)
    273             , m_isolate(isolate)
    274         {
    275         }
    276 
    277         virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE
    278         {
    279             if (classId != v8DOMNodeClassId)
    280                 return;
    281             // Casting to Handle is safe here, since the Persistent cannot get
    282             // GCd during visiting.
    283             v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value);
    284             ASSERT_UNUSED(m_isolate, V8Node::hasInstance(*wrapper, m_isolate));
    285             ASSERT((*wrapper)->IsObject());
    286             m_visitor->visitNode(V8Node::toNative(*wrapper));
    287         }
    288 
    289     private:
    290         WrappedNodeVisitor* m_visitor;
    291         v8::Isolate* m_isolate;
    292     } wrapperVisitor(visitor, isolate);
    293 
    294     v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
    295 }
    296 
    297 ProfileNameIdleTimeMap* ScriptProfiler::currentProfileNameIdleTimeMap()
    298 {
    299     AtomicallyInitializedStatic(WTF::ThreadSpecific<ProfileNameIdleTimeMap>*, map = new WTF::ThreadSpecific<ProfileNameIdleTimeMap>);
    300     return *map;
    301 }
    302 
    303 void ScriptProfiler::setIdle(bool isIdle)
    304 {
    305     v8::Isolate* isolate = v8::Isolate::GetCurrent();
    306     if (v8::CpuProfiler* profiler = isolate->GetCpuProfiler())
    307         profiler->SetIdle(isIdle);
    308 }
    309 
    310 } // namespace WebCore
    311