Commit 3903817e authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] introduced memory size limit for console message storage

Without this CL we have only limit for amount of console messages and if user are dumping a huge messages we pretty soon run out of memory.
So let's introduce limit for memory consumption it would help chromium and Node.js as well.

BUG=chromium:671489
R=dgozman@chomium.org,alph@chromium.org, hpayer@chromium.org, ulan@chromium.org

Review-Url: https://codereview.chromium.org/2653293003
Cr-Commit-Position: refs/heads/master@{#42780}
parent 475b455b
......@@ -9361,6 +9361,15 @@ void debug::ResetBlackboxedStateCache(Isolate* v8_isolate,
}
}
int debug::EstimatedValueSize(Isolate* v8_isolate, v8::Local<v8::Value> value) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
ENTER_V8(isolate);
i::Handle<i::Object> object = Utils::OpenHandle(*value);
if (object->IsSmi()) return i::kPointerSize;
CHECK(object->IsHeapObject());
return i::Handle<i::HeapObject>::cast(object)->Size();
}
Local<String> CpuProfileNode::GetFunctionName() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
i::Isolate* isolate = node->isolate();
......
......@@ -175,6 +175,8 @@ void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener);
void ResetBlackboxedStateCache(Isolate* isolate,
v8::Local<debug::Script> script);
int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value);
} // namespace debug
} // namespace v8
......
......@@ -4,6 +4,7 @@
#include "src/inspector/v8-console-message.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/string-util.h"
......@@ -58,6 +59,7 @@ String16 consoleAPITypeValue(ConsoleAPIType type) {
}
const unsigned maxConsoleMessageCount = 1000;
const int maxConsoleMessageV8Size = 10 * 1024 * 1024;
const unsigned maxArrayItemsLimit = 10000;
const unsigned maxStackDepthLimit = 32;
......@@ -371,9 +373,12 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
message->m_stackTrace = std::move(stackTrace);
message->m_type = type;
message->m_contextId = contextId;
for (size_t i = 0; i < arguments.size(); ++i)
for (size_t i = 0; i < arguments.size(); ++i) {
message->m_arguments.push_back(std::unique_ptr<v8::Global<v8::Value>>(
new v8::Global<v8::Value>(isolate, arguments.at(i))));
message->m_v8Size +=
v8::debug::EstimatedValueSize(isolate, arguments.at(i));
}
if (arguments.size())
message->m_message = V8ValueStringBuilder::toString(arguments[0], context);
......@@ -418,6 +423,8 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(
consoleMessage->m_arguments.push_back(
std::unique_ptr<v8::Global<v8::Value>>(
new v8::Global<v8::Value>(isolate, exception)));
consoleMessage->m_v8Size +=
v8::debug::EstimatedValueSize(isolate, exception);
}
return consoleMessage;
}
......@@ -438,15 +445,14 @@ void V8ConsoleMessage::contextDestroyed(int contextId) {
if (m_message.isEmpty()) m_message = "<message collected>";
Arguments empty;
m_arguments.swap(empty);
m_v8Size = 0;
}
// ------------------------ V8ConsoleMessageStorage ----------------------------
V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
int contextGroupId)
: m_inspector(inspector),
m_contextGroupId(contextGroupId),
m_expiredCount(0) {}
: m_inspector(inspector), m_contextGroupId(contextGroupId) {}
V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); }
......@@ -467,23 +473,33 @@ void V8ConsoleMessageStorage::addMessage(
DCHECK(m_messages.size() <= maxConsoleMessageCount);
if (m_messages.size() == maxConsoleMessageCount) {
++m_expiredCount;
m_estimatedSize -= m_messages.front()->estimatedSize();
m_messages.pop_front();
}
while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size &&
!m_messages.empty()) {
m_estimatedSize -= m_messages.front()->estimatedSize();
m_messages.pop_front();
}
m_messages.push_back(std::move(message));
m_estimatedSize += m_messages.back()->estimatedSize();
}
void V8ConsoleMessageStorage::clear() {
m_messages.clear();
m_expiredCount = 0;
m_estimatedSize = 0;
if (V8InspectorSessionImpl* session =
m_inspector->sessionForContextGroup(m_contextGroupId))
session->releaseObjectGroup("console");
}
void V8ConsoleMessageStorage::contextDestroyed(int contextId) {
for (size_t i = 0; i < m_messages.size(); ++i)
m_estimatedSize = 0;
for (size_t i = 0; i < m_messages.size(); ++i) {
m_messages[i]->contextDestroyed(contextId);
m_estimatedSize += m_messages[i]->estimatedSize();
}
}
} // namespace v8_inspector
......@@ -65,6 +65,10 @@ class V8ConsoleMessage {
ConsoleAPIType type() const;
void contextDestroyed(int contextId);
int estimatedSize() const {
return m_v8Size + static_cast<int>(m_message.length() * sizeof(UChar));
}
private:
V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message);
......@@ -89,6 +93,7 @@ class V8ConsoleMessage {
ConsoleAPIType m_type;
unsigned m_exceptionId;
unsigned m_revokedExceptionId;
int m_v8Size = 0;
Arguments m_arguments;
String16 m_detailedMessage;
};
......@@ -99,7 +104,6 @@ class V8ConsoleMessageStorage {
~V8ConsoleMessageStorage();
int contextGroupId() { return m_contextGroupId; }
int expiredCount() { return m_expiredCount; }
const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const {
return m_messages;
}
......@@ -111,7 +115,7 @@ class V8ConsoleMessageStorage {
private:
V8InspectorImpl* m_inspector;
int m_contextGroupId;
int m_expiredCount;
int m_estimatedSize = 0;
std::deque<std::unique_ptr<V8ConsoleMessage>> m_messages;
};
......
Checks that console message storage doesn't exceed limits
Running test: testMaxConsoleMessagesCount
Messages reported: 1000
Running test: testMaxConsoleMessagesV8Size
Messages reported: 3
// Copyright 2017 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.
print('Checks that console message storage doesn\'t exceed limits');
InspectorTest.addScript(`
function generateEmptyMessages(n) {
for (var i = 0; i < n; ++i) {
console.log('');
}
}
function generate1MbMessages(n) {
for (var i = 0; i < n; ++i) {
console.log(new Array(1024 * 1024 - 32).join('!'));
}
}
//# sourceURL=test.js`, 7, 26);
var messagesReported = 0;
Protocol.Runtime.onConsoleAPICalled(message => {
++messagesReported;
});
InspectorTest.runTestSuite([
function testMaxConsoleMessagesCount(next) {
messagesReported = 0;
Protocol.Runtime.evaluate({ expression: 'generateEmptyMessages(1005)'})
.then(() => Protocol.Runtime.enable())
.then(() => Protocol.Runtime.disable())
.then(() => InspectorTest.log(`Messages reported: ${messagesReported}`))
.then(next);
},
function testMaxConsoleMessagesV8Size(next) {
messagesReported = 0;
Protocol.Runtime.evaluate({ expression: 'generate1MbMessages(11)'})
.then(() => Protocol.Runtime.enable())
.then(() => Protocol.Runtime.disable())
.then(() => InspectorTest.log(`Messages reported: ${messagesReported}`))
.then(next);
}
]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment