v8-heap-profiler-agent-impl.cc 16.6 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "src/inspector/v8-heap-profiler-agent-impl.h"
6

7 8 9 10
#include "include/v8-inspector.h"
#include "include/v8-profiler.h"
#include "include/v8-version.h"
#include "src/base/platform/mutex.h"
11
#include "src/inspector/injected-script.h"
12
#include "src/inspector/inspected-context.h"
13
#include "src/inspector/protocol/Protocol.h"
14 15 16 17
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
18 19 20 21 22 23 24 25 26 27 28 29

namespace v8_inspector {

namespace {

namespace HeapProfilerAgentState {
static const char heapProfilerEnabled[] = "heapProfilerEnabled";
static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled";
static const char allocationTrackingEnabled[] = "allocationTrackingEnabled";
static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled";
static const char samplingHeapProfilerInterval[] =
    "samplingHeapProfilerInterval";
30
}  // namespace HeapProfilerAgentState
31 32 33

class HeapSnapshotProgress final : public v8::ActivityControl {
 public:
34
  explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
      : m_frontend(frontend) {}
  ControlOption ReportProgressValue(int done, int total) override {
    m_frontend->reportHeapSnapshotProgress(done, total,
                                           protocol::Maybe<bool>());
    if (done >= total) {
      m_frontend->reportHeapSnapshotProgress(total, total, true);
    }
    m_frontend->flush();
    return kContinue;
  }

 private:
  protocol::HeapProfiler::Frontend* m_frontend;
};

class GlobalObjectNameResolver final
    : public v8::HeapProfiler::ObjectNameResolver {
 public:
  explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session)
      : m_offset(0), m_strings(10000), m_session(session) {}

  const char* GetName(v8::Local<v8::Object> object) override {
    InspectedContext* context = m_session->inspector()->getContext(
        m_session->contextGroupId(),
59
        InspectedContext::contextId(object->CreationContext()));
60 61 62 63 64 65
    if (!context) return "";
    String16 name = context->origin();
    size_t length = name.length();
    if (m_offset + length + 1 >= m_strings.size()) return "";
    for (size_t i = 0; i < length; ++i) {
      UChar ch = name[i];
66
      m_strings[m_offset + i] = ch > 0xFF ? '?' : static_cast<char>(ch);
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    }
    m_strings[m_offset + length] = '\0';
    char* result = &*m_strings.begin() + m_offset;
    m_offset += length + 1;
    return result;
  }

 private:
  size_t m_offset;
  std::vector<char> m_strings;
  V8InspectorSessionImpl* m_session;
};

class HeapSnapshotOutputStream final : public v8::OutputStream {
 public:
82
  explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend)
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
      : m_frontend(frontend) {}
  void EndOfStream() override {}
  int GetChunkSize() override { return 102400; }
  WriteResult WriteAsciiChunk(char* data, int size) override {
    m_frontend->addHeapSnapshotChunk(String16(data, size));
    m_frontend->flush();
    return kContinue;
  }

 private:
  protocol::HeapProfiler::Frontend* m_frontend;
};

v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) {
  v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
  v8::Local<v8::Value> value = profiler->FindObjectById(id);
  if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>();
  return value.As<v8::Object>();
}

class InspectableHeapObject final : public V8InspectorSession::Inspectable {
 public:
  explicit InspectableHeapObject(int heapObjectId)
      : m_heapObjectId(heapObjectId) {}
  v8::Local<v8::Value> get(v8::Local<v8::Context> context) override {
    return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId);
  }

 private:
  int m_heapObjectId;
};

class HeapStatsStream final : public v8::OutputStream {
 public:
117
  explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend)
118 119 120 121 122 123 124 125 126 127 128 129
      : m_frontend(frontend) {}

  void EndOfStream() override {}

  WriteResult WriteAsciiChunk(char* data, int size) override {
    DCHECK(false);
    return kAbort;
  }

  WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData,
                                  int count) override {
    DCHECK_GT(count, 0);
130
    auto statsDiff = std::make_unique<protocol::Array<int>>();
131
    for (int i = 0; i < count; ++i) {
132 133 134
      statsDiff->emplace_back(updateData[i].index);
      statsDiff->emplace_back(updateData[i].count);
      statsDiff->emplace_back(updateData[i].size);
135 136 137 138 139 140 141 142 143 144 145
    }
    m_frontend->heapStatsUpdate(std::move(statsDiff));
    return kContinue;
  }

 private:
  protocol::HeapProfiler::Frontend* m_frontend;
};

}  // namespace

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
struct V8HeapProfilerAgentImpl::AsyncGC {
  v8::base::Mutex m_mutex;
  bool m_canceled = false;
  bool m_pending = false;
  std::vector<std::unique_ptr<CollectGarbageCallback>> m_pending_callbacks;
};

class V8HeapProfilerAgentImpl::GCTask : public v8::Task {
 public:
  GCTask(v8::Isolate* isolate, std::shared_ptr<AsyncGC> async_gc)
      : m_isolate(isolate), m_async_gc(async_gc) {}

  void Run() override {
    std::shared_ptr<AsyncGC> async_gc = m_async_gc.lock();
    if (!async_gc) return;
    v8::base::MutexGuard lock(&async_gc->m_mutex);
    if (async_gc->m_canceled) return;
    v8::debug::ForceGarbageCollection(
        m_isolate, v8::EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
    for (auto& callback : async_gc->m_pending_callbacks) {
      callback->sendSuccess();
    }
    async_gc->m_pending_callbacks.clear();
  }

 private:
  v8::Isolate* m_isolate;
  std::weak_ptr<AsyncGC> m_async_gc;
};

176 177 178 179 180 181 182
V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
    V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
    protocol::DictionaryValue* state)
    : m_session(session),
      m_isolate(session->inspector()->isolate()),
      m_frontend(frontendChannel),
      m_state(state),
183 184
      m_hasTimer(false),
      m_async_gc(std::make_shared<AsyncGC>()) {}
185

186 187 188 189 190
V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {
  v8::base::MutexGuard lock(&m_async_gc->m_mutex);
  m_async_gc->m_canceled = true;
  m_async_gc->m_pending_callbacks.clear();
}
191 192 193 194 195 196 197 198 199 200 201 202 203 204

void V8HeapProfilerAgentImpl::restore() {
  if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
                               false))
    m_frontend.resetProfiles();
  if (m_state->booleanProperty(
          HeapProfilerAgentState::heapObjectsTrackingEnabled, false))
    startTrackingHeapObjectsInternal(m_state->booleanProperty(
        HeapProfilerAgentState::allocationTrackingEnabled, false));
  if (m_state->booleanProperty(
          HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
    double samplingInterval = m_state->doubleProperty(
        HeapProfilerAgentState::samplingHeapProfilerInterval, -1);
    DCHECK_GE(samplingInterval, 0);
205
    startSampling(Maybe<double>(samplingInterval));
206 207 208
  }
}

209 210 211 212 213 214 215 216 217
void V8HeapProfilerAgentImpl::collectGarbage(
    std::unique_ptr<CollectGarbageCallback> callback) {
  v8::base::MutexGuard lock(&m_async_gc->m_mutex);
  m_async_gc->m_pending_callbacks.push_back(std::move(callback));
  if (!m_async_gc->m_pending) {
    v8::debug::GetCurrentPlatform()
        ->GetForegroundTaskRunner(m_isolate)
        ->PostNonNestableTask(std::make_unique<GCTask>(m_isolate, m_async_gc));
  }
218 219
}

220 221
Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
    Maybe<bool> trackAllocations) {
222 223 224 225 226
  m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true);
  bool allocationTrackingEnabled = trackAllocations.fromMaybe(false);
  m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled,
                      allocationTrackingEnabled);
  startTrackingHeapObjectsInternal(allocationTrackingEnabled);
227
  return Response::Success();
228 229
}

230
Response V8HeapProfilerAgentImpl::stopTrackingHeapObjects(
231
    Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) {
232
  requestHeapStatsUpdate();
233 234
  takeHeapSnapshot(std::move(reportProgress),
                   std::move(treatGlobalObjectsAsRoots));
235
  stopTrackingHeapObjectsInternal();
236
  return Response::Success();
237 238
}

239
Response V8HeapProfilerAgentImpl::enable() {
240
  m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true);
241
  return Response::Success();
242 243
}

244
Response V8HeapProfilerAgentImpl::disable() {
245 246 247 248 249 250 251 252
  stopTrackingHeapObjectsInternal();
  if (m_state->booleanProperty(
          HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) {
    v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
    if (profiler) profiler->StopSamplingHeapProfiler();
  }
  m_isolate->GetHeapProfiler()->ClearObjectIds();
  m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false);
253
  return Response::Success();
254 255
}

256 257
Response V8HeapProfilerAgentImpl::takeHeapSnapshot(
    Maybe<bool> reportProgress, Maybe<bool> treatGlobalObjectsAsRoots) {
258
  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
259
  if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
260 261
  std::unique_ptr<HeapSnapshotProgress> progress;
  if (reportProgress.fromMaybe(false))
262
    progress.reset(new HeapSnapshotProgress(&m_frontend));
263 264

  GlobalObjectNameResolver resolver(m_session);
265 266
  const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(
      progress.get(), &resolver, treatGlobalObjectsAsRoots.fromMaybe(true));
267
  if (!snapshot) return Response::ServerError("Failed to take heap snapshot");
268 269 270
  HeapSnapshotOutputStream stream(&m_frontend);
  snapshot->Serialize(&stream);
  const_cast<v8::HeapSnapshot*>(snapshot)->Delete();
271
  return Response::Success();
272 273
}

274 275
Response V8HeapProfilerAgentImpl::getObjectByHeapObjectId(
    const String16& heapSnapshotObjectId, Maybe<String16> objectGroup,
276 277 278
    std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
  bool ok;
  int id = heapSnapshotObjectId.toInteger(&ok);
279
  if (!ok) return Response::ServerError("Invalid heap snapshot object id");
280 281 282

  v8::HandleScope handles(m_isolate);
  v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
283 284
  if (heapObject.IsEmpty())
    return Response::ServerError("Object is not available");
285

286
  if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
287
    return Response::ServerError("Object is not available");
288 289 290

  *result = m_session->wrapObject(heapObject->CreationContext(), heapObject,
                                  objectGroup.fromMaybe(""), false);
291 292
  if (!*result) return Response::ServerError("Object is not available");
  return Response::Success();
293 294
}

295 296
Response V8HeapProfilerAgentImpl::addInspectedHeapObject(
    const String16& inspectedHeapObjectId) {
297 298
  bool ok;
  int id = inspectedHeapObjectId.toInteger(&ok);
299
  if (!ok) return Response::ServerError("Invalid heap snapshot object id");
300 301 302

  v8::HandleScope handles(m_isolate);
  v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id);
303 304
  if (heapObject.IsEmpty())
    return Response::ServerError("Object is not available");
305

306
  if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject))
307
    return Response::ServerError("Object is not available");
308 309
  m_session->addInspectedObject(
      std::unique_ptr<InspectableHeapObject>(new InspectableHeapObject(id)));
310
  return Response::Success();
311 312
}

313 314
Response V8HeapProfilerAgentImpl::getHeapObjectId(
    const String16& objectId, String16* heapSnapshotObjectId) {
315 316 317
  v8::HandleScope handles(m_isolate);
  v8::Local<v8::Value> value;
  v8::Local<v8::Context> context;
318 319
  Response response =
      m_session->unwrapObject(objectId, &value, &context, nullptr);
320
  if (!response.IsSuccess()) return response;
321
  if (value->IsUndefined()) return Response::InternalError();
322 323

  v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value);
324
  *heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id));
325
  return Response::Success();
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
}

void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() {
  HeapStatsStream stream(&m_frontend);
  v8::SnapshotObjectId lastSeenObjectId =
      m_isolate->GetHeapProfiler()->GetHeapStats(&stream);
  m_frontend.lastSeenObjectId(
      lastSeenObjectId, m_session->inspector()->client()->currentTimeMS());
}

// static
void V8HeapProfilerAgentImpl::onTimer(void* data) {
  reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate();
}

void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal(
    bool trackAllocations) {
  m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations);
  if (!m_hasTimer) {
    m_hasTimer = true;
    m_session->inspector()->client()->startRepeatingTimer(
        0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this));
  }
}

void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() {
  if (m_hasTimer) {
    m_session->inspector()->client()->cancelTimer(
        reinterpret_cast<void*>(this));
    m_hasTimer = false;
  }
  m_isolate->GetHeapProfiler()->StopTrackingHeapObjects();
  m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled,
                      false);
  m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false);
}

363 364
Response V8HeapProfilerAgentImpl::startSampling(
    Maybe<double> samplingInterval) {
365
  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
366
  if (!profiler) return Response::ServerError("Cannot access v8 heap profiler");
367 368 369 370 371 372 373 374 375 376
  const unsigned defaultSamplingInterval = 1 << 15;
  double samplingIntervalValue =
      samplingInterval.fromMaybe(defaultSamplingInterval);
  m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval,
                     samplingIntervalValue);
  m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
                      true);
  profiler->StartSamplingHeapProfiler(
      static_cast<uint64_t>(samplingIntervalValue), 128,
      v8::HeapProfiler::kSamplingForceGC);
377
  return Response::Success();
378 379 380 381
}

namespace {
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode>
382 383
buildSampingHeapProfileNode(v8::Isolate* isolate,
                            const v8::AllocationProfile::Node* node) {
384
  auto children = std::make_unique<
385
      protocol::Array<protocol::HeapProfiler::SamplingHeapProfileNode>>();
386
  for (const auto* child : node->children)
387
    children->emplace_back(buildSampingHeapProfileNode(isolate, child));
388 389 390 391 392
  size_t selfSize = 0;
  for (const auto& allocation : node->allocations)
    selfSize += allocation.size * allocation.count;
  std::unique_ptr<protocol::Runtime::CallFrame> callFrame =
      protocol::Runtime::CallFrame::create()
393
          .setFunctionName(toProtocolString(isolate, node->name))
394
          .setScriptId(String16::fromInteger(node->script_id))
395
          .setUrl(toProtocolString(isolate, node->script_name))
396 397 398 399 400 401 402 403
          .setLineNumber(node->line_number - 1)
          .setColumnNumber(node->column_number - 1)
          .build();
  std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result =
      protocol::HeapProfiler::SamplingHeapProfileNode::create()
          .setCallFrame(std::move(callFrame))
          .setSelfSize(selfSize)
          .setChildren(std::move(children))
404
          .setId(node->node_id)
405 406 407 408 409
          .build();
  return result;
}
}  // namespace

410
Response V8HeapProfilerAgentImpl::stopSampling(
411
    std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
412
  Response result = getSamplingProfile(profile);
413
  if (result.IsSuccess()) {
414 415 416 417 418 419 420 421 422
    m_isolate->GetHeapProfiler()->StopSamplingHeapProfiler();
    m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled,
                        false);
  }
  return result;
}

Response V8HeapProfilerAgentImpl::getSamplingProfile(
    std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) {
423
  v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
424 425
  // Need a scope as v8::AllocationProfile contains Local handles.
  v8::HandleScope scope(m_isolate);
426 427
  std::unique_ptr<v8::AllocationProfile> v8Profile(
      profiler->GetAllocationProfile());
428
  if (!v8Profile)
429
    return Response::ServerError("V8 sampling heap profiler was not started.");
430
  v8::AllocationProfile::Node* root = v8Profile->GetRootNode();
431
  auto samples = std::make_unique<
432
      protocol::Array<protocol::HeapProfiler::SamplingHeapProfileSample>>();
433
  for (const auto& sample : v8Profile->GetSamples()) {
434 435 436 437 438 439
    samples->emplace_back(
        protocol::HeapProfiler::SamplingHeapProfileSample::create()
            .setSize(sample.size * sample.count)
            .setNodeId(sample.node_id)
            .setOrdinal(static_cast<double>(sample.sample_id))
            .build());
440
  }
441
  *profile = protocol::HeapProfiler::SamplingHeapProfile::create()
442
                 .setHead(buildSampingHeapProfileNode(m_isolate, root))
443
                 .setSamples(std::move(samples))
444
                 .build();
445
  return Response::Success();
446 447 448
}

}  // namespace v8_inspector