summaryrefslogtreecommitdiff
path: root/runtime/debugger.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/debugger.cc')
-rw-r--r--runtime/debugger.cc132
1 files changed, 114 insertions, 18 deletions
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index d0b50fe820..5b46ba10ab 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -18,7 +18,9 @@
#include <sys/uio.h>
+#include <memory>
#include <set>
+#include <vector>
#include "android-base/stringprintf.h"
@@ -38,7 +40,7 @@
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/large_object_space.h"
#include "gc/space/space-inl.h"
-#include "handle_scope.h"
+#include "handle_scope-inl.h"
#include "jdwp/jdwp_priv.h"
#include "jdwp/object_registry.h"
#include "jni_internal.h"
@@ -56,7 +58,7 @@
#include "scoped_thread_state_change-inl.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
-#include "handle_scope-inl.h"
+#include "stack.h"
#include "thread_list.h"
#include "utf.h"
#include "well_known_classes.h"
@@ -4918,24 +4920,81 @@ void Dbg::DumpRecentAllocations() {
}
class StringTable {
+ private:
+ struct Entry {
+ explicit Entry(const char* data_in)
+ : data(data_in), hash(ComputeModifiedUtf8Hash(data_in)), index(0) {
+ }
+ Entry(const Entry& entry) = default;
+ Entry(Entry&& entry) = default;
+
+ // Pointer to the actual string data.
+ const char* data;
+
+ // The hash of the data.
+ const uint32_t hash;
+
+ // The index. This will be filled in on Finish and is not part of the ordering, so mark it
+ // mutable.
+ mutable uint32_t index;
+
+ bool operator==(const Entry& other) const {
+ return strcmp(data, other.data) == 0;
+ }
+ };
+ struct EntryHash {
+ size_t operator()(const Entry& entry) const {
+ return entry.hash;
+ }
+ };
+
public:
- StringTable() {
+ StringTable() : finished_(false) {
}
- void Add(const std::string& str) {
- table_.insert(str);
+ void Add(const char* str, bool copy_string) {
+ DCHECK(!finished_);
+ if (UNLIKELY(copy_string)) {
+ // Check whether it's already there.
+ Entry entry(str);
+ if (table_.find(entry) != table_.end()) {
+ return;
+ }
+
+ // Make a copy.
+ size_t str_len = strlen(str);
+ char* copy = new char[str_len + 1];
+ strncpy(copy, str, str_len + 1);
+ string_backup_.emplace_back(copy);
+ str = copy;
+ }
+ Entry entry(str);
+ table_.insert(entry);
}
- void Add(const char* str) {
- table_.insert(str);
+ // Update all entries and give them an index. Note that this is likely not the insertion order,
+ // as the set will with high likelihood reorder elements. Thus, Add must not be called after
+ // Finish, and Finish must be called before IndexOf. In that case, WriteTo will walk in
+ // the same order as Finish, and indices will agree. The order invariant, as well as indices,
+ // are enforced through debug checks.
+ void Finish() {
+ DCHECK(!finished_);
+ finished_ = true;
+ uint32_t index = 0;
+ for (auto& entry : table_) {
+ entry.index = index;
+ ++index;
+ }
}
size_t IndexOf(const char* s) const {
- auto it = table_.find(s);
+ DCHECK(finished_);
+ Entry entry(s);
+ auto it = table_.find(entry);
if (it == table_.end()) {
LOG(FATAL) << "IndexOf(\"" << s << "\") failed";
}
- return std::distance(table_.begin(), it);
+ return it->index;
}
size_t Size() const {
@@ -4943,17 +5002,24 @@ class StringTable {
}
void WriteTo(std::vector<uint8_t>& bytes) const {
- for (const std::string& str : table_) {
- const char* s = str.c_str();
- size_t s_len = CountModifiedUtf8Chars(s);
+ DCHECK(finished_);
+ uint32_t cur_index = 0;
+ for (const auto& entry : table_) {
+ DCHECK_EQ(cur_index++, entry.index);
+
+ size_t s_len = CountModifiedUtf8Chars(entry.data);
std::unique_ptr<uint16_t[]> s_utf16(new uint16_t[s_len]);
- ConvertModifiedUtf8ToUtf16(s_utf16.get(), s);
+ ConvertModifiedUtf8ToUtf16(s_utf16.get(), entry.data);
JDWP::AppendUtf16BE(bytes, s_utf16.get(), s_len);
}
}
private:
- std::set<std::string> table_;
+ std::unordered_set<Entry, EntryHash> table_;
+ std::vector<std::unique_ptr<char[]>> string_backup_;
+
+ bool finished_;
+
DISALLOW_COPY_AND_ASSIGN(StringTable);
};
@@ -5034,21 +5100,40 @@ jbyteArray Dbg::GetRecentAllocations() {
StringTable method_names;
StringTable filenames;
+ VLOG(jdwp) << "Collecting StringTables.";
+
const uint16_t capped_count = CappedAllocRecordCount(records->GetRecentAllocationSize());
uint16_t count = capped_count;
+ size_t alloc_byte_count = 0;
for (auto it = records->RBegin(), end = records->REnd();
count > 0 && it != end; count--, it++) {
const gc::AllocRecord* record = &it->second;
std::string temp;
- class_names.Add(record->GetClassDescriptor(&temp));
+ const char* class_descr = record->GetClassDescriptor(&temp);
+ class_names.Add(class_descr, !temp.empty());
+
+ // Size + tid + class name index + stack depth.
+ alloc_byte_count += 4u + 2u + 2u + 1u;
+
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
ArtMethod* m = record->StackElement(i).GetMethod();
- class_names.Add(m->GetDeclaringClassDescriptor());
- method_names.Add(m->GetName());
- filenames.Add(GetMethodSourceFile(m));
+ class_names.Add(m->GetDeclaringClassDescriptor(), false);
+ method_names.Add(m->GetName(), false);
+ filenames.Add(GetMethodSourceFile(m), false);
}
+
+ // Depth * (class index + method name index + file name index + line number).
+ alloc_byte_count += record->GetDepth() * (2u + 2u + 2u + 2u);
}
+ class_names.Finish();
+ method_names.Finish();
+ filenames.Finish();
+ VLOG(jdwp) << "Done collecting StringTables:" << std::endl
+ << " ClassNames: " << class_names.Size() << std::endl
+ << " MethodNames: " << method_names.Size() << std::endl
+ << " Filenames: " << filenames.Size();
+
LOG(INFO) << "recent allocation records: " << capped_count;
LOG(INFO) << "allocation records all objects: " << records->Size();
@@ -5078,6 +5163,12 @@ jbyteArray Dbg::GetRecentAllocations() {
JDWP::Append2BE(bytes, method_names.Size());
JDWP::Append2BE(bytes, filenames.Size());
+ VLOG(jdwp) << "Dumping allocations with stacks";
+
+ // Enlarge the vector for the allocation data.
+ size_t reserve_size = bytes.size() + alloc_byte_count;
+ bytes.reserve(reserve_size);
+
std::string temp;
count = capped_count;
// The last "count" number of allocation records in "records" are the most recent "count" number
@@ -5115,6 +5206,9 @@ jbyteArray Dbg::GetRecentAllocations() {
}
}
+ CHECK_EQ(bytes.size(), reserve_size);
+ VLOG(jdwp) << "Dumping tables.";
+
// (xb) class name strings
// (xb) method name strings
// (xb) source file strings
@@ -5122,6 +5216,8 @@ jbyteArray Dbg::GetRecentAllocations() {
class_names.WriteTo(bytes);
method_names.WriteTo(bytes);
filenames.WriteTo(bytes);
+
+ VLOG(jdwp) << "GetRecentAllocations: data created. " << bytes.size();
}
JNIEnv* env = self->GetJniEnv();
jbyteArray result = env->NewByteArray(bytes.size());