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 "V8Node.h" 35#include "V8Window.h" 36#include "bindings/v8/RetainedDOMInfo.h" 37#include "bindings/v8/ScriptObject.h" 38#include "bindings/v8/V8Binding.h" 39#include "bindings/v8/V8DOMWrapper.h" 40#include "bindings/v8/WrapperTypeInfo.h" 41#include "core/dom/Document.h" 42#include "core/inspector/BindingVisitors.h" 43 44#include <v8-profiler.h> 45#include <v8.h> 46 47#include "wtf/ThreadSpecific.h" 48 49namespace WebCore { 50 51typedef HashMap<String, double> ProfileNameIdleTimeMap; 52 53void ScriptProfiler::setSamplingInterval(int intervalUs) 54{ 55 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 56 v8::CpuProfiler* profiler = isolate->GetCpuProfiler(); 57 if (profiler) 58 profiler->SetSamplingInterval(intervalUs); 59} 60 61void ScriptProfiler::start(const String& title) 62{ 63 ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap(); 64 if (profileNameIdleTimeMap->contains(title)) 65 return; 66 profileNameIdleTimeMap->add(title, 0); 67 68 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 69 v8::CpuProfiler* profiler = isolate->GetCpuProfiler(); 70 if (!profiler) 71 return; 72 v8::HandleScope handleScope(isolate); 73 profiler->StartCpuProfiling(v8String(isolate, title), true); 74} 75 76PassRefPtr<ScriptProfile> ScriptProfiler::stop(const String& title) 77{ 78 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 79 v8::CpuProfiler* profiler = isolate->GetCpuProfiler(); 80 if (!profiler) 81 return 0; 82 v8::HandleScope handleScope(isolate); 83 const v8::CpuProfile* profile = profiler->StopCpuProfiling(v8String(isolate, title)); 84 if (!profile) 85 return 0; 86 87 String profileTitle = toCoreString(profile->GetTitle()); 88 double idleTime = 0.0; 89 ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap(); 90 ProfileNameIdleTimeMap::iterator profileIdleTime = profileNameIdleTimeMap->find(profileTitle); 91 if (profileIdleTime != profileNameIdleTimeMap->end()) { 92 idleTime = profileIdleTime->value * 1000.0; 93 profileNameIdleTimeMap->remove(profileIdleTime); 94 } 95 96 return ScriptProfile::create(profile, idleTime); 97} 98 99void ScriptProfiler::collectGarbage() 100{ 101 v8::V8::LowMemoryNotification(); 102} 103 104ScriptObject ScriptProfiler::objectByHeapObjectId(unsigned id) 105{ 106 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 107 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); 108 if (!profiler) 109 return ScriptObject(); 110 // As ids are unique, it doesn't matter which HeapSnapshot owns HeapGraphNode. 111 // We need to find first HeapSnapshot containing a node with the specified id. 112 const v8::HeapGraphNode* node = 0; 113 for (int i = 0, l = profiler->GetSnapshotCount(); i < l; ++i) { 114 const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i); 115 node = snapshot->GetNodeById(id); 116 if (node) 117 break; 118 } 119 if (!node) 120 return ScriptObject(); 121 122 v8::HandleScope handleScope(isolate); 123 v8::Handle<v8::Value> value = node->GetHeapValue(); 124 if (!value->IsObject()) 125 return ScriptObject(); 126 127 v8::Handle<v8::Object> object = value.As<v8::Object>(); 128 129 if (object->InternalFieldCount() >= v8DefaultWrapperInternalFieldCount) { 130 v8::Handle<v8::Value> wrapper = object->GetInternalField(v8DOMWrapperObjectIndex); 131 // Skip wrapper boilerplates which are like regular wrappers but don't have 132 // native object. 133 if (!wrapper.IsEmpty() && wrapper->IsUndefined()) 134 return ScriptObject(); 135 } 136 137 ScriptState* scriptState = ScriptState::forContext(object->CreationContext()); 138 return ScriptObject(scriptState, object); 139} 140 141unsigned ScriptProfiler::getHeapObjectId(const ScriptValue& value) 142{ 143 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 144 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); 145 v8::SnapshotObjectId id = profiler->GetObjectId(value.v8Value()); 146 return id; 147} 148 149namespace { 150 151class ActivityControlAdapter : public v8::ActivityControl { 152public: 153 ActivityControlAdapter(ScriptProfiler::HeapSnapshotProgress* progress) 154 : m_progress(progress), m_firstReport(true) { } 155 ControlOption ReportProgressValue(int done, int total) 156 { 157 ControlOption result = m_progress->isCanceled() ? kAbort : kContinue; 158 if (m_firstReport) { 159 m_firstReport = false; 160 m_progress->Start(total); 161 } else 162 m_progress->Worked(done); 163 if (done >= total) 164 m_progress->Done(); 165 return result; 166 } 167private: 168 ScriptProfiler::HeapSnapshotProgress* m_progress; 169 bool m_firstReport; 170}; 171 172class GlobalObjectNameResolver : public v8::HeapProfiler::ObjectNameResolver { 173public: 174 virtual const char* GetName(v8::Handle<v8::Object> object) 175 { 176 if (V8DOMWrapper::isWrapperOfType(object, &V8Window::wrapperTypeInfo)) { 177 DOMWindow* window = V8Window::toNative(object); 178 if (window) { 179 CString url = window->document()->url().string().utf8(); 180 m_strings.append(url); 181 return url.data(); 182 } 183 } 184 return 0; 185 } 186 187private: 188 Vector<CString> m_strings; 189}; 190 191} // namespace 192 193void ScriptProfiler::startTrackingHeapObjects() 194{ 195 v8::Isolate::GetCurrent()->GetHeapProfiler()->StartTrackingHeapObjects(); 196} 197 198namespace { 199 200class HeapStatsStream : public v8::OutputStream { 201public: 202 HeapStatsStream(ScriptProfiler::OutputStream* stream) : m_stream(stream) { } 203 virtual void EndOfStream() OVERRIDE { } 204 205 virtual WriteResult WriteAsciiChunk(char* data, int size) OVERRIDE 206 { 207 ASSERT(false); 208 return kAbort; 209 } 210 211 virtual WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, int count) OVERRIDE 212 { 213 Vector<uint32_t> rawData(count * 3); 214 for (int i = 0; i < count; ++i) { 215 int offset = i * 3; 216 rawData[offset] = updateData[i].index; 217 rawData[offset + 1] = updateData[i].count; 218 rawData[offset + 2] = updateData[i].size; 219 } 220 m_stream->write(rawData.data(), rawData.size()); 221 return kContinue; 222 } 223 224private: 225 ScriptProfiler::OutputStream* m_stream; 226}; 227 228} 229 230unsigned ScriptProfiler::requestHeapStatsUpdate(ScriptProfiler::OutputStream* stream) 231{ 232 HeapStatsStream heapStatsStream(stream); 233 return v8::Isolate::GetCurrent()->GetHeapProfiler()->GetHeapStats(&heapStatsStream); 234} 235 236void ScriptProfiler::stopTrackingHeapObjects() 237{ 238 v8::Isolate::GetCurrent()->GetHeapProfiler()->StopTrackingHeapObjects(); 239} 240 241// FIXME: This method should receive a ScriptState, from which we should retrieve an Isolate. 242PassRefPtr<ScriptHeapSnapshot> ScriptProfiler::takeHeapSnapshot(const String& title, HeapSnapshotProgress* control) 243{ 244 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 245 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); 246 if (!profiler) 247 return 0; 248 v8::HandleScope handleScope(isolate); 249 ASSERT(control); 250 ActivityControlAdapter adapter(control); 251 GlobalObjectNameResolver resolver; 252 const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(v8String(isolate, title), &adapter, &resolver); 253 return snapshot ? ScriptHeapSnapshot::create(snapshot) : 0; 254} 255 256static v8::RetainedObjectInfo* retainedDOMInfo(uint16_t classId, v8::Handle<v8::Value> wrapper) 257{ 258 ASSERT(classId == v8DOMNodeClassId); 259 if (!wrapper->IsObject()) 260 return 0; 261 Node* node = V8Node::toNative(wrapper.As<v8::Object>()); 262 return node ? new RetainedDOMInfo(node) : 0; 263} 264 265void ScriptProfiler::initialize() 266{ 267 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 268 v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); 269 if (profiler) 270 profiler->SetWrapperClassInfoProvider(v8DOMNodeClassId, &retainedDOMInfo); 271} 272 273void ScriptProfiler::visitNodeWrappers(WrappedNodeVisitor* visitor) 274{ 275 // visitNodeWrappers() should receive a ScriptState and retrieve an Isolate 276 // from the ScriptState. 277 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 278 v8::HandleScope handleScope(isolate); 279 280 class DOMNodeWrapperVisitor : public v8::PersistentHandleVisitor { 281 public: 282 DOMNodeWrapperVisitor(WrappedNodeVisitor* visitor, v8::Isolate* isolate) 283 : m_visitor(visitor) 284 , m_isolate(isolate) 285 { 286 } 287 288 virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE 289 { 290 if (classId != v8DOMNodeClassId) 291 return; 292 // Casting to Handle is safe here, since the Persistent cannot get 293 // GCd during visiting. 294 v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value); 295 ASSERT_UNUSED(m_isolate, V8Node::hasInstanceInAnyWorld(*wrapper, m_isolate)); 296 ASSERT((*wrapper)->IsObject()); 297 m_visitor->visitNode(V8Node::toNative(*wrapper)); 298 } 299 300 private: 301 WrappedNodeVisitor* m_visitor; 302 v8::Isolate* m_isolate; 303 } wrapperVisitor(visitor, isolate); 304 305 v8::V8::VisitHandlesWithClassIds(&wrapperVisitor); 306} 307 308ProfileNameIdleTimeMap* ScriptProfiler::currentProfileNameIdleTimeMap() 309{ 310 AtomicallyInitializedStatic(WTF::ThreadSpecific<ProfileNameIdleTimeMap>*, map = new WTF::ThreadSpecific<ProfileNameIdleTimeMap>); 311 return *map; 312} 313 314void ScriptProfiler::setIdle(bool isIdle) 315{ 316 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 317 if (v8::CpuProfiler* profiler = isolate->GetCpuProfiler()) 318 profiler->SetIdle(isIdle); 319} 320 321} // namespace WebCore 322