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, ...@@ -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 { Local<String> CpuProfileNode::GetFunctionName() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
i::Isolate* isolate = node->isolate(); i::Isolate* isolate = node->isolate();
......
...@@ -175,6 +175,8 @@ void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener); ...@@ -175,6 +175,8 @@ void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener);
void ResetBlackboxedStateCache(Isolate* isolate, void ResetBlackboxedStateCache(Isolate* isolate,
v8::Local<debug::Script> script); v8::Local<debug::Script> script);
int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value);
} // namespace debug } // namespace debug
} // namespace v8 } // namespace v8
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "src/inspector/v8-console-message.h" #include "src/inspector/v8-console-message.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/inspected-context.h" #include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h" #include "src/inspector/protocol/Protocol.h"
#include "src/inspector/string-util.h" #include "src/inspector/string-util.h"
...@@ -58,6 +59,7 @@ String16 consoleAPITypeValue(ConsoleAPIType type) { ...@@ -58,6 +59,7 @@ String16 consoleAPITypeValue(ConsoleAPIType type) {
} }
const unsigned maxConsoleMessageCount = 1000; const unsigned maxConsoleMessageCount = 1000;
const int maxConsoleMessageV8Size = 10 * 1024 * 1024;
const unsigned maxArrayItemsLimit = 10000; const unsigned maxArrayItemsLimit = 10000;
const unsigned maxStackDepthLimit = 32; const unsigned maxStackDepthLimit = 32;
...@@ -371,9 +373,12 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI( ...@@ -371,9 +373,12 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
message->m_stackTrace = std::move(stackTrace); message->m_stackTrace = std::move(stackTrace);
message->m_type = type; message->m_type = type;
message->m_contextId = contextId; 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>>( message->m_arguments.push_back(std::unique_ptr<v8::Global<v8::Value>>(
new v8::Global<v8::Value>(isolate, arguments.at(i)))); new v8::Global<v8::Value>(isolate, arguments.at(i))));
message->m_v8Size +=
v8::debug::EstimatedValueSize(isolate, arguments.at(i));
}
if (arguments.size()) if (arguments.size())
message->m_message = V8ValueStringBuilder::toString(arguments[0], context); message->m_message = V8ValueStringBuilder::toString(arguments[0], context);
...@@ -418,6 +423,8 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException( ...@@ -418,6 +423,8 @@ std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(
consoleMessage->m_arguments.push_back( consoleMessage->m_arguments.push_back(
std::unique_ptr<v8::Global<v8::Value>>( std::unique_ptr<v8::Global<v8::Value>>(
new v8::Global<v8::Value>(isolate, exception))); new v8::Global<v8::Value>(isolate, exception)));
consoleMessage->m_v8Size +=
v8::debug::EstimatedValueSize(isolate, exception);
} }
return consoleMessage; return consoleMessage;
} }
...@@ -438,15 +445,14 @@ void V8ConsoleMessage::contextDestroyed(int contextId) { ...@@ -438,15 +445,14 @@ void V8ConsoleMessage::contextDestroyed(int contextId) {
if (m_message.isEmpty()) m_message = "<message collected>"; if (m_message.isEmpty()) m_message = "<message collected>";
Arguments empty; Arguments empty;
m_arguments.swap(empty); m_arguments.swap(empty);
m_v8Size = 0;
} }
// ------------------------ V8ConsoleMessageStorage ---------------------------- // ------------------------ V8ConsoleMessageStorage ----------------------------
V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector, V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector,
int contextGroupId) int contextGroupId)
: m_inspector(inspector), : m_inspector(inspector), m_contextGroupId(contextGroupId) {}
m_contextGroupId(contextGroupId),
m_expiredCount(0) {}
V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); } V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); }
...@@ -467,23 +473,33 @@ void V8ConsoleMessageStorage::addMessage( ...@@ -467,23 +473,33 @@ void V8ConsoleMessageStorage::addMessage(
DCHECK(m_messages.size() <= maxConsoleMessageCount); DCHECK(m_messages.size() <= maxConsoleMessageCount);
if (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.pop_front();
} }
m_messages.push_back(std::move(message)); m_messages.push_back(std::move(message));
m_estimatedSize += m_messages.back()->estimatedSize();
} }
void V8ConsoleMessageStorage::clear() { void V8ConsoleMessageStorage::clear() {
m_messages.clear(); m_messages.clear();
m_expiredCount = 0; m_estimatedSize = 0;
if (V8InspectorSessionImpl* session = if (V8InspectorSessionImpl* session =
m_inspector->sessionForContextGroup(m_contextGroupId)) m_inspector->sessionForContextGroup(m_contextGroupId))
session->releaseObjectGroup("console"); session->releaseObjectGroup("console");
} }
void V8ConsoleMessageStorage::contextDestroyed(int contextId) { 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_messages[i]->contextDestroyed(contextId);
m_estimatedSize += m_messages[i]->estimatedSize();
}
} }
} // namespace v8_inspector } // namespace v8_inspector
...@@ -65,6 +65,10 @@ class V8ConsoleMessage { ...@@ -65,6 +65,10 @@ class V8ConsoleMessage {
ConsoleAPIType type() const; ConsoleAPIType type() const;
void contextDestroyed(int contextId); void contextDestroyed(int contextId);
int estimatedSize() const {
return m_v8Size + static_cast<int>(m_message.length() * sizeof(UChar));
}
private: private:
V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message); V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message);
...@@ -89,6 +93,7 @@ class V8ConsoleMessage { ...@@ -89,6 +93,7 @@ class V8ConsoleMessage {
ConsoleAPIType m_type; ConsoleAPIType m_type;
unsigned m_exceptionId; unsigned m_exceptionId;
unsigned m_revokedExceptionId; unsigned m_revokedExceptionId;
int m_v8Size = 0;
Arguments m_arguments; Arguments m_arguments;
String16 m_detailedMessage; String16 m_detailedMessage;
}; };
...@@ -99,7 +104,6 @@ class V8ConsoleMessageStorage { ...@@ -99,7 +104,6 @@ class V8ConsoleMessageStorage {
~V8ConsoleMessageStorage(); ~V8ConsoleMessageStorage();
int contextGroupId() { return m_contextGroupId; } int contextGroupId() { return m_contextGroupId; }
int expiredCount() { return m_expiredCount; }
const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const { const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const {
return m_messages; return m_messages;
} }
...@@ -111,7 +115,7 @@ class V8ConsoleMessageStorage { ...@@ -111,7 +115,7 @@ class V8ConsoleMessageStorage {
private: private:
V8InspectorImpl* m_inspector; V8InspectorImpl* m_inspector;
int m_contextGroupId; int m_contextGroupId;
int m_expiredCount; int m_estimatedSize = 0;
std::deque<std::unique_ptr<V8ConsoleMessage>> m_messages; 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