1// Copyright 2013 The Chromium 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 "chrome/test/chromedriver/chrome/heap_snapshot_taker.h" 6 7#include "base/json/json_reader.h" 8#include "base/logging.h" 9#include "base/values.h" 10#include "chrome/test/chromedriver/chrome/devtools_client.h" 11#include "chrome/test/chromedriver/chrome/status.h" 12 13HeapSnapshotTaker::HeapSnapshotTaker(DevToolsClient* client) 14 : client_(client), snapshot_uid_(-1) { 15 client_->AddListener(this); 16} 17 18HeapSnapshotTaker::~HeapSnapshotTaker() {} 19 20Status HeapSnapshotTaker::TakeSnapshot(scoped_ptr<base::Value>* snapshot) { 21 Status status1 = TakeSnapshotInternal(); 22 base::DictionaryValue params; 23 Status status2 = client_->SendCommand("Debugger.disable", params); 24 Status status3(kOk); 25 if (snapshot_uid_ != -1) { // Clear the snapshot cached in chrome. 26 status3 = client_->SendCommand("HeapProfiler.clearProfiles", params); 27 } 28 29 Status status4(kOk); 30 if (status1.IsOk() && status2.IsOk() && status3.IsOk()) { 31 scoped_ptr<base::Value> value(base::JSONReader::Read(snapshot_)); 32 if (!value) 33 status4 = Status(kUnknownError, "heap snapshot not in JSON format"); 34 else 35 *snapshot = value.Pass(); 36 } 37 snapshot_uid_ = -1; 38 snapshot_.clear(); 39 if (status1.IsError()) 40 return status1; 41 else if (status2.IsError()) 42 return status2; 43 else if (status3.IsError()) 44 return status3; 45 else 46 return status4; 47} 48 49Status HeapSnapshotTaker::TakeSnapshotInternal() { 50 if (snapshot_uid_ != -1) 51 return Status(kUnknownError, "unexpected heap snapshot was triggered"); 52 53 base::DictionaryValue params; 54 const char* kMethods[] = { 55 "Debugger.enable", 56 "HeapProfiler.collectGarbage", 57 "HeapProfiler.takeHeapSnapshot" 58 }; 59 for (size_t i = 0; i < arraysize(kMethods); ++i) { 60 Status status = client_->SendCommand(kMethods[i], params); 61 if (status.IsError()) 62 return status; 63 } 64 65 if (snapshot_uid_ == -1) 66 return Status(kUnknownError, "failed to receive snapshot uid"); 67 68 base::DictionaryValue uid_params; 69 uid_params.SetInteger("uid", snapshot_uid_); 70 Status status = client_->SendCommand( 71 "HeapProfiler.getHeapSnapshot", uid_params); 72 if (status.IsError()) 73 return status; 74 75 return Status(kOk); 76} 77 78Status HeapSnapshotTaker::OnEvent(DevToolsClient* client, 79 const std::string& method, 80 const base::DictionaryValue& params) { 81 if (method == "HeapProfiler.addProfileHeader") { 82 if (snapshot_uid_ != -1) { 83 LOG(WARNING) << "multiple heap snapshot triggered"; 84 } else if (!params.GetInteger("header.uid", &snapshot_uid_)) { 85 return Status(kUnknownError, 86 "HeapProfiler.addProfileHeader has invalid 'header.uid'"); 87 } 88 } else if (method == "HeapProfiler.addHeapSnapshotChunk") { 89 int uid = -1; 90 if (!params.GetInteger("uid", &uid)) { 91 return Status(kUnknownError, 92 "HeapProfiler.addHeapSnapshotChunk has no 'uid'"); 93 } else if (uid == snapshot_uid_) { 94 std::string chunk; 95 if (!params.GetString("chunk", &chunk)) { 96 return Status(kUnknownError, 97 "HeapProfiler.addHeapSnapshotChunk has no 'chunk'"); 98 } 99 100 snapshot_.append(chunk); 101 } else { 102 LOG(WARNING) << "expect chunk event uid " << snapshot_uid_ 103 << ", but got " << uid; 104 } 105 } 106 return Status(kOk); 107} 108