[debugger] Add handle printing capabilities.
Adds a "handle" command that prints a table of all handles in the
process by default, and prints more details if a specific handle is
given.
Adds string decoder functions for the various handle values.
Bug: 43944
Change-Id: I2a33793c063bc67b9679567aaa23fe9d5092477a
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/402821
Reviewed-by: Dangyi Liu <dangyi@google.com>
Testability-Review: Brett Wilson <brettw@google.com>
Commit-Queue: Brett Wilson <brettw@google.com>
diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn
index 2abda1c..b7a2730 100644
--- a/src/developer/debug/shared/BUILD.gn
+++ b/src/developer/debug/shared/BUILD.gn
@@ -15,6 +15,8 @@
"buffered_fd.h",
"component_utils.cc",
"component_utils.h",
+ "handle_info.cc",
+ "handle_info.h",
"message_loop.cc",
"message_loop.h",
"regex.cc",
@@ -76,6 +78,7 @@
sources = [
"address_range_unittest.cc",
"component_utils_unittest.cc",
+ "handle_info_unittest.cc",
"message_loop_unittest.cc",
"regex_unittest.cc",
"stream_buffer_unittest.cc",
diff --git a/src/developer/debug/shared/handle_info.cc b/src/developer/debug/shared/handle_info.cc
new file mode 100644
index 0000000..ebf1f67a
--- /dev/null
+++ b/src/developer/debug/shared/handle_info.cc
@@ -0,0 +1,140 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/developer/debug/shared/handle_info.h"
+
+namespace debug_ipc {
+
+std::string HandleTypeToString(uint32_t handle_type) {
+ // Don't use Zircon headers from here, so need to hardcode the values.
+ switch (handle_type) {
+ case 0u:
+ return "ZX_OBJ_TYPE_NONE";
+ case 1u:
+ return "ZX_OBJ_TYPE_PROCESS";
+ case 2u:
+ return "ZX_OBJ_TYPE_THREAD";
+ case 3u:
+ return "ZX_OBJ_TYPE_VMO";
+ case 4u:
+ return "ZX_OBJ_TYPE_CHANNEL";
+ case 5u:
+ return "ZX_OBJ_TYPE_EVENT";
+ case 6u:
+ return "ZX_OBJ_TYPE_PORT";
+ case 9u:
+ return "ZX_OBJ_TYPE_INTERRUPT";
+ case 11u:
+ return "ZX_OBJ_TYPE_PCI_DEVICE";
+ case 12u:
+ return "ZX_OBJ_TYPE_LOG";
+ case 14u:
+ return "ZX_OBJ_TYPE_SOCKET";
+ case 15u:
+ return "ZX_OBJ_TYPE_RESOURCE";
+ case 16u:
+ return "ZX_OBJ_TYPE_EVENTPAIR";
+ case 17u:
+ return "ZX_OBJ_TYPE_JOB";
+ case 18u:
+ return "ZX_OBJ_TYPE_VMAR";
+ case 19u:
+ return "ZX_OBJ_TYPE_FIFO";
+ case 20u:
+ return "ZX_OBJ_TYPE_GUEST";
+ case 21u:
+ return "ZX_OBJ_TYPE_VCPU";
+ case 22u:
+ return "ZX_OBJ_TYPE_TIMER";
+ case 23u:
+ return "ZX_OBJ_TYPE_IOMMU";
+ case 24u:
+ return "ZX_OBJ_TYPE_BTI";
+ case 25u:
+ return "ZX_OBJ_TYPE_PROFILE";
+ case 26u:
+ return "ZX_OBJ_TYPE_PMT";
+ case 27u:
+ return "ZX_OBJ_TYPE_SUSPEND_TOKEN";
+ case 28u:
+ return "ZX_OBJ_TYPE_PAGER";
+ case 29u:
+ return "ZX_OBJ_TYPE_EXCEPTION";
+ case 30u:
+ return "ZX_OBJ_TYPE_CLOCK";
+ case 31u:
+ return "ZX_OBJ_TYPE_STREAM";
+ case 32u:
+ return "ZX_OBJ_TYPE_MSI_ALLOCATION";
+ case 33u:
+ return "ZX_OBJ_TYPE_MSI_INTERRUPT";
+ default:
+ return "<unknown (" + std::to_string(handle_type) + ")>";
+ }
+}
+
+std::string HandlePropsToString(uint32_t handle_props) {
+ if (handle_props == 0)
+ return "ZX_OBJ_PROP_NONE";
+
+ // Currently this is just one possible value. It may be extended to be a bitfield in the future.
+ // In that case, we probably want a version that returns an array like the "handle rights" one.
+ if (handle_props == 1)
+ return "ZX_OBJ_PROP_WAITABLE";
+
+ return "<unknown (" + std::to_string(handle_props) + ")>";
+}
+
+std::vector<std::string> HandleRightsToStrings(uint32_t handle_rights) {
+ std::vector<std::string> result;
+ if (handle_rights == 0) {
+ result.emplace_back("ZX_RIGHT_NONE");
+ return result;
+ }
+
+ struct RightMapping {
+ uint32_t bit_value;
+ const char* name;
+ };
+ static const RightMapping kRightMapping[] = {
+ {1u << 0, "ZX_RIGHT_DUPLICATE"}, {1u << 1, "ZX_RIGHT_TRANSFER"},
+ {1u << 2, "ZX_RIGHT_READ"}, {1u << 3, "ZX_RIGHT_WRITE"},
+ {1u << 4, "ZX_RIGHT_EXECUTE"}, {1u << 5, "ZX_RIGHT_MAP"},
+ {1u << 6, "ZX_RIGHT_GET_PROPERTY"}, {1u << 7, "ZX_RIGHT_SET_PROPERTY"},
+ {1u << 8, "ZX_RIGHT_ENUMERATE"}, {1u << 9, "ZX_RIGHT_DESTROY"},
+ {1u << 10, "ZX_RIGHT_SET_POLICY"}, {1u << 11, "ZX_RIGHT_GET_POLICY"},
+ {1u << 12, "ZX_RIGHT_SIGNAL"}, {1u << 13, "ZX_RIGHT_SIGNAL_PEER"},
+ {1u << 14, "ZX_RIGHT_WAIT"}, {1u << 15, "ZX_RIGHT_INSPECT"},
+ {1u << 16, "ZX_RIGHT_MANAGE_JOB"}, {1u << 17, "ZX_RIGHT_MANAGE_PROCESS"},
+ {1u << 18, "ZX_RIGHT_MANAGE_THREAD"}, {1u << 19, "<unknown (1 << 19)>"},
+ {1u << 20, "<unknown (1 << 20)>"}, {1u << 21, "<unknown (1 << 21)>"},
+ {1u << 22, "<unknown (1 << 22)>"}, {1u << 23, "<unknown (1 << 23)>"},
+ {1u << 24, "<unknown (1 << 24)>"}, {1u << 25, "<unknown (1 << 25)>"},
+ {1u << 26, "<unknown (1 << 26)>"}, {1u << 27, "<unknown (1 << 27)>"},
+ {1u << 28, "<unknown (1 << 28)>"}, {1u << 29, "<unknown (1 << 29)>"},
+ {1u << 30, "<unknown (1 << 30)>"}, {1u << 31, "ZX_RIGHT_SAME_RIGHTS"},
+ };
+
+ for (const auto& mapping : kRightMapping) {
+ if (handle_rights & mapping.bit_value)
+ result.emplace_back(mapping.name);
+ }
+
+ return result;
+}
+
+std::string HandleRightsToString(uint32_t handle_rights) {
+ auto rights = HandleRightsToStrings(handle_rights);
+
+ std::string result;
+ for (size_t i = 0; i < rights.size(); i++) {
+ if (i > 0)
+ result += " | ";
+ result += rights[i];
+ }
+
+ return result;
+}
+
+} // namespace debug_ipc
diff --git a/src/developer/debug/shared/handle_info.h b/src/developer/debug/shared/handle_info.h
new file mode 100644
index 0000000..2534d1c
--- /dev/null
+++ b/src/developer/debug/shared/handle_info.h
@@ -0,0 +1,29 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_DEVELOPER_DEBUG_SHARED_HANDLE_INFO_H_
+#define SRC_DEVELOPER_DEBUG_SHARED_HANDLE_INFO_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+namespace debug_ipc {
+
+// Converts a uint32_t handle type to a string. Returns "<unknown>" on failure.
+std::string HandleTypeToString(uint32_t handle_type);
+
+// Returns "<none>" when unset, and "<unknown>" if unknown.
+std::string HandlePropsToString(uint32_t handle_props);
+
+// Returns a vector of strings, one for each right set.
+std::vector<std::string> HandleRightsToStrings(uint32_t handle_rights);
+
+// Returns a vector of right strings separated by "|" characters.
+std::string HandleRightsToString(uint32_t handle_rights);
+
+} // namespace debug_ipc
+
+#endif // SRC_DEVELOPER_DEBUG_SHARED_HANDLE_INFO_H_
diff --git a/src/developer/debug/shared/handle_info_unittest.cc b/src/developer/debug/shared/handle_info_unittest.cc
new file mode 100644
index 0000000..46ce686
--- /dev/null
+++ b/src/developer/debug/shared/handle_info_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/developer/debug/shared/handle_info.h"
+
+#include <gtest/gtest.h>
+
+namespace debug_ipc {
+
+TEST(HandleInfo, HandleTypeToString) {
+ EXPECT_EQ("ZX_OBJ_TYPE_NONE", HandleTypeToString(0u));
+ EXPECT_EQ("ZX_OBJ_TYPE_SOCKET", HandleTypeToString(14u));
+ std::string a = HandleTypeToString(9999);
+ EXPECT_EQ("<unknown (9999)>", a);
+}
+
+TEST(HandleInfo, HandlePropsToString) {
+ EXPECT_EQ("ZX_OBJ_PROP_NONE", HandlePropsToString(0));
+ EXPECT_EQ("ZX_OBJ_PROP_WAITABLE", HandlePropsToString(1));
+ EXPECT_EQ("<unknown (999)>", HandlePropsToString(999));
+}
+
+TEST(HandleInfo, HandleRightsToString) {
+ EXPECT_EQ("ZX_RIGHT_NONE", HandleRightsToString(0));
+ EXPECT_EQ("ZX_RIGHT_DUPLICATE", HandleRightsToString(1));
+ EXPECT_EQ("ZX_RIGHT_TRANSFER", HandleRightsToString(2));
+ EXPECT_EQ("ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER", HandleRightsToString(3));
+ EXPECT_EQ("ZX_RIGHT_DUPLICATE | <unknown (1 << 29)> | ZX_RIGHT_SAME_RIGHTS",
+ HandleRightsToString(0b10100000'00000000'00000000'00000001));
+}
+
+} // namespace debug_ipc
diff --git a/src/developer/debug/zxdb/console/BUILD.gn b/src/developer/debug/zxdb/console/BUILD.gn
index fc5c644..3883fde4 100644
--- a/src/developer/debug/zxdb/console/BUILD.gn
+++ b/src/developer/debug/zxdb/console/BUILD.gn
@@ -55,6 +55,8 @@
"commands/verb_enable.h",
"commands/verb_finish.cc",
"commands/verb_finish.h",
+ "commands/verb_handle.cc",
+ "commands/verb_handle.h",
"commands/verb_help.cc",
"commands/verb_help.h",
"commands/verb_jump.cc",
@@ -139,6 +141,8 @@
"format_exception.h",
"format_frame.cc",
"format_frame.h",
+ "format_handle.cc",
+ "format_handle.h",
"format_job.cc",
"format_job.h",
"format_location.cc",
@@ -232,6 +236,7 @@
"format_context_unittest.cc",
"format_exception_unittest.cc",
"format_frame_unittest.cc",
+ "format_handle_unittest.cc",
"format_job_unittest.cc",
"format_location_unittest.cc",
"format_memory_unittest.cc",
diff --git a/src/developer/debug/zxdb/console/commands/verb_handle.cc b/src/developer/debug/zxdb/console/commands/verb_handle.cc
new file mode 100644
index 0000000..5ae353d
--- /dev/null
+++ b/src/developer/debug/zxdb/console/commands/verb_handle.cc
@@ -0,0 +1,120 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/developer/debug/zxdb/console/commands/verb_handle.h"
+
+#include "src/developer/debug/zxdb/client/process.h"
+#include "src/developer/debug/zxdb/client/target.h"
+#include "src/developer/debug/zxdb/console/command.h"
+#include "src/developer/debug/zxdb/console/command_utils.h"
+#include "src/developer/debug/zxdb/console/console.h"
+#include "src/developer/debug/zxdb/console/format_handle.h"
+#include "src/developer/debug/zxdb/console/output_buffer.h"
+#include "src/developer/debug/zxdb/console/verbs.h"
+
+namespace zxdb {
+
+namespace {
+
+constexpr int kHexSwitch = 1;
+
+const char kHandleShortHelp[] = "handle[s]: Print handle list or details.";
+const char kHandleHelp[] =
+ R"(handle[s] [ <handle-expression> ]
+
+ With no arguments, prints all handles for the process.
+
+ If an expression is given, the information corresponding to the resulting
+ handle value will be printed.
+
+ 👉 See "help expressions" for how to write expressions.
+
+Options
+
+ -x
+ Print numbers as hexadecimal. Defaults to decimal.
+
+Examples
+
+ handle
+ process 1 handles
+ Print all handles for the current/given process.
+
+ handle -x h
+ handle -x some_object->handle
+ Prints the information for the given handle.
+)";
+
+void OnEvalComplete(fxl::RefPtr<EvalContext> eval_context, fxl::WeakPtr<Process> weak_process,
+ ErrOrValue value, bool hex) {
+ Console* console = Console::get();
+ if (!weak_process)
+ return console->Output(Err("Process exited while requesting handles."));
+ if (value.has_error())
+ return console->Output(value.err());
+
+ uint64_t handle_value = 0;
+ if (Err err = value.value().PromoteTo64(&handle_value); err.has_error())
+ return console->Output(err);
+
+ weak_process->LoadInfoHandleTable([handle_value, hex](
+ ErrOr<std::vector<debug_ipc::InfoHandleExtended>> handles) {
+ Console* console = Console::get();
+ if (handles.has_error())
+ return console->Output(handles.err());
+
+ // Find the handle in the table.
+ for (const auto& handle : handles.value()) {
+ if (handle.handle_value == handle_value)
+ return console->Output(FormatHandle(handle, hex));
+ }
+ console->Output("No handle with value " + std::to_string(handle_value) + " in the process.");
+ });
+}
+
+Err RunVerbHandle(ConsoleContext* context, const Command& cmd) {
+ if (Err err = AssertRunningTarget(context, "handle", cmd.target()); err.has_error())
+ return err;
+
+ bool hex = cmd.HasSwitch(kHexSwitch);
+
+ if (cmd.args().empty()) {
+ cmd.target()->GetProcess()->LoadInfoHandleTable(
+ [hex](ErrOr<std::vector<debug_ipc::InfoHandleExtended>> handles) {
+ Console* console = Console::get();
+ if (handles.has_error())
+ return console->Output(handles.err());
+
+ auto handles_sorted = handles.take_value();
+ std::sort(
+ handles_sorted.begin(), handles_sorted.end(),
+ [](const debug_ipc::InfoHandleExtended& a, const debug_ipc::InfoHandleExtended& b) {
+ return a.handle_value < b.handle_value;
+ });
+ console->Output(FormatHandles(handles_sorted, hex));
+ });
+ } else {
+ // Evaluate the expression, then print just that handle.
+ fxl::RefPtr<EvalContext> eval_context = GetEvalContextForCommand(cmd);
+ return EvalCommandExpression(
+ cmd, "handle", eval_context, false, false,
+ [eval_context, weak_process = cmd.target()->GetProcess()->GetWeakPtr(),
+ hex](ErrOrValue value) {
+ OnEvalComplete(eval_context, weak_process, std::move(value), hex);
+ });
+ }
+ return Err();
+}
+
+} // namespace
+
+VerbRecord GetHandleVerbRecord() {
+ VerbRecord handle(&RunVerbHandle, {"handle", "handles"}, kHandleShortHelp, kHandleHelp,
+ CommandGroup::kQuery);
+ handle.param_type = VerbRecord::kOneParam;
+ handle.switches.emplace_back(kHexSwitch, false, "", 'x');
+ return handle;
+}
+
+} // namespace zxdb
diff --git a/src/developer/debug/zxdb/console/commands/verb_handle.h b/src/developer/debug/zxdb/console/commands/verb_handle.h
new file mode 100644
index 0000000..e2e3166
--- /dev/null
+++ b/src/developer/debug/zxdb/console/commands/verb_handle.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_COMMANDS_VERB_HANDLE_H_
+#define SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_COMMANDS_VERB_HANDLE_H_
+
+namespace zxdb {
+
+struct VerbRecord;
+
+VerbRecord GetHandleVerbRecord();
+
+} // namespace zxdb
+
+#endif // SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_COMMANDS_VERB_HANDLE_H_
diff --git a/src/developer/debug/zxdb/console/commands/verb_print.cc b/src/developer/debug/zxdb/console/commands/verb_print.cc
index 5ceb30b..e05b202 100644
--- a/src/developer/debug/zxdb/console/commands/verb_print.cc
+++ b/src/developer/debug/zxdb/console/commands/verb_print.cc
@@ -58,7 +58,6 @@
if (options.has_error())
return options.err();
- auto data_provider = eval_context->GetDataProvider();
return EvalCommandExpression(
cmd, "print", eval_context, false, false,
[options = options.value(), eval_context](ErrOrValue value) {
diff --git a/src/developer/debug/zxdb/console/format_handle.cc b/src/developer/debug/zxdb/console/format_handle.cc
new file mode 100644
index 0000000..547cd23
--- /dev/null
+++ b/src/developer/debug/zxdb/console/format_handle.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/developer/debug/zxdb/console/format_handle.h"
+
+#include "src/developer/debug/ipc/records.h"
+#include "src/developer/debug/shared/handle_info.h"
+#include "src/developer/debug/zxdb/common/string_util.h"
+#include "src/developer/debug/zxdb/console/format_table.h"
+#include "src/developer/debug/zxdb/console/string_util.h"
+
+namespace zxdb {
+
+namespace {
+
+// Appends a two element string vector to the given output.
+void AppendTwoEltRow(const std::string& a, const std::string& b,
+ std::vector<std::vector<std::string>>& rows) {
+ auto& row = rows.emplace_back();
+ row.push_back(a);
+ row.push_back(b);
+}
+
+template <class T>
+std::string NumToString(T value, bool hex) {
+ if (hex)
+ return to_hex_string(value);
+ return std::to_string(value);
+}
+
+} // namespace
+
+OutputBuffer FormatHandles(const std::vector<debug_ipc::InfoHandleExtended>& handles, bool hex) {
+ if (handles.empty())
+ return OutputBuffer("No handles.");
+
+ std::vector<std::vector<std::string>> rows;
+ for (const auto& handle : handles) {
+ auto& row = rows.emplace_back();
+ row.push_back(NumToString(handle.handle_value, hex));
+ row.push_back(debug_ipc::HandleTypeToString(handle.type));
+ row.push_back(NumToString(handle.koid, hex));
+ }
+
+ OutputBuffer out;
+ FormatTable({ColSpec(Align::kRight, 0, "Handle", 2), ColSpec(Align::kLeft, 0, "Type", 1),
+ ColSpec(Align::kRight, 0, "Koid", 1)},
+ rows, &out);
+ return out;
+}
+
+OutputBuffer FormatHandle(const debug_ipc::InfoHandleExtended& handle, bool hex) {
+ std::vector<std::vector<std::string>> rows;
+ AppendTwoEltRow("Type", debug_ipc::HandleTypeToString(handle.type), rows);
+ AppendTwoEltRow("Value", NumToString(handle.handle_value, hex), rows);
+
+ // Put each right on a separate line.
+ std::vector<std::string> rights = debug_ipc::HandleRightsToStrings(handle.rights);
+ for (size_t i = 0; i < rights.size(); i++) {
+ if (i == 0)
+ AppendTwoEltRow("Rights", rights[i], rows);
+ else
+ AppendTwoEltRow(std::string(), rights[i], rows);
+ }
+
+ AppendTwoEltRow("Properties", debug_ipc::HandlePropsToString(handle.props), rows);
+ AppendTwoEltRow("Koid", NumToString(handle.koid, hex), rows);
+ if (handle.related_koid)
+ AppendTwoEltRow("Related koid", NumToString(handle.related_koid, hex), rows);
+ if (handle.peer_owner_koid)
+ AppendTwoEltRow("Peer-owner koid", NumToString(handle.peer_owner_koid, hex), rows);
+
+ OutputBuffer out;
+ FormatTable({ColSpec(Align::kRight, 0, std::string(), 2, Syntax::kHeading),
+ ColSpec(Align::kLeft, 0, std::string(), 1)},
+ rows, &out);
+ return out;
+}
+
+} // namespace zxdb
diff --git a/src/developer/debug/zxdb/console/format_handle.h b/src/developer/debug/zxdb/console/format_handle.h
new file mode 100644
index 0000000..a889284
--- /dev/null
+++ b/src/developer/debug/zxdb/console/format_handle.h
@@ -0,0 +1,29 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_FORMAT_HANDLE_H_
+#define SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_FORMAT_HANDLE_H_
+
+#include <vector>
+
+#include "src/developer/debug/zxdb/console/output_buffer.h"
+
+namespace debug_ipc {
+struct InfoHandleExtended;
+}
+
+namespace zxdb {
+
+// Formats a table of the handles with minimal information. The order of the table will be the
+// same as the input vector. The hex flag prints values in hexadecimal. Otherwise decimal will be
+// used.
+OutputBuffer FormatHandles(const std::vector<debug_ipc::InfoHandleExtended>& handles, bool hex);
+
+// Formats a detailed summary of a single handle's information. The hex flag prints values in
+// hexadecimal. Otherwise decimal will be used.
+OutputBuffer FormatHandle(const debug_ipc::InfoHandleExtended& handle, bool hex);
+
+} // namespace zxdb
+
+#endif // SRC_DEVELOPER_DEBUG_ZXDB_CONSOLE_FORMAT_HANDLE_H_
diff --git a/src/developer/debug/zxdb/console/format_handle_unittest.cc b/src/developer/debug/zxdb/console/format_handle_unittest.cc
new file mode 100644
index 0000000..0b972fc
--- /dev/null
+++ b/src/developer/debug/zxdb/console/format_handle_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/developer/debug/zxdb/console/format_handle.h"
+
+#include <gtest/gtest.h>
+
+#include "src/developer/debug/ipc/records.h"
+
+namespace zxdb {
+
+TEST(FormatHandle, Table) {
+ std::vector<debug_ipc::InfoHandleExtended> handles;
+
+ // Empty case.
+ OutputBuffer out = FormatHandles(handles, false);
+ EXPECT_EQ("No handles.", out.AsString());
+
+ // Give it two objects.
+ handles.resize(2);
+ handles[0].type = 2; // Thread.
+ handles[0].handle_value = 1234;
+ handles[0].koid = 7890;
+
+ handles[1].type = 1; // Process.
+ handles[1].handle_value = 1123;
+ handles[1].koid = 7891;
+
+ out = FormatHandles(handles, false);
+ EXPECT_EQ(
+ " Handle Type Koid\n"
+ " 1234 ZX_OBJ_TYPE_THREAD 7890\n"
+ " 1123 ZX_OBJ_TYPE_PROCESS 7891\n",
+ out.AsString());
+
+ // Hex formatting
+ out = FormatHandles(handles, true);
+ EXPECT_EQ(
+ " Handle Type Koid\n"
+ " 0x4d2 ZX_OBJ_TYPE_THREAD 0x1ed2\n"
+ " 0x463 ZX_OBJ_TYPE_PROCESS 0x1ed3\n",
+ out.AsString());
+}
+
+TEST(FormatHandle, BasicDetails) {
+ debug_ipc::InfoHandleExtended handle;
+ handle.type = 2; // Thread.
+ handle.handle_value = 1234;
+ handle.rights = 3;
+ handle.props = 0;
+ handle.koid = 7890;
+ handle.related_koid = 1111;
+ handle.peer_owner_koid = 2222;
+
+ OutputBuffer out = FormatHandle(handle, false);
+ EXPECT_EQ(
+ " Type ZX_OBJ_TYPE_THREAD\n"
+ " Value 1234\n"
+ " Rights ZX_RIGHT_DUPLICATE\n"
+ " ZX_RIGHT_TRANSFER\n"
+ " Properties ZX_OBJ_PROP_NONE\n"
+ " Koid 7890\n"
+ " Related koid 1111\n"
+ " Peer-owner koid 2222\n",
+ out.AsString());
+
+ // Related and peer owner koid should be omitted when 0 (not all handle types have these and
+ // it looks confusing). This one also tests hex formatting.
+ handle.props = 1;
+ handle.related_koid = 0;
+ handle.peer_owner_koid = 0;
+ out = FormatHandle(handle, true);
+ EXPECT_EQ(
+ " Type ZX_OBJ_TYPE_THREAD\n"
+ " Value 0x4d2\n"
+ " Rights ZX_RIGHT_DUPLICATE\n"
+ " ZX_RIGHT_TRANSFER\n"
+ " Properties ZX_OBJ_PROP_WAITABLE\n"
+ " Koid 0x1ed2\n",
+ out.AsString());
+}
+
+} // namespace zxdb
diff --git a/src/developer/debug/zxdb/console/verbs.cc b/src/developer/debug/zxdb/console/verbs.cc
index dd067e8..21825c4 100644
--- a/src/developer/debug/zxdb/console/verbs.cc
+++ b/src/developer/debug/zxdb/console/verbs.cc
@@ -24,6 +24,7 @@
#include "src/developer/debug/zxdb/console/commands/verb_down.h"
#include "src/developer/debug/zxdb/console/commands/verb_enable.h"
#include "src/developer/debug/zxdb/console/commands/verb_finish.h"
+#include "src/developer/debug/zxdb/console/commands/verb_handle.h"
#include "src/developer/debug/zxdb/console/commands/verb_help.h"
#include "src/developer/debug/zxdb/console/commands/verb_jump.h"
#include "src/developer/debug/zxdb/console/commands/verb_kill.h"
@@ -130,6 +131,7 @@
all_verbs[Verb::kDown] = GetDownVerbRecord();
all_verbs[Verb::kEnable] = GetEnableVerbRecord();
all_verbs[Verb::kFinish] = GetFinishVerbRecord();
+ all_verbs[Verb::kHandle] = GetHandleVerbRecord();
all_verbs[Verb::kHelp] = GetHelpVerbRecord();
all_verbs[Verb::kJump] = GetJumpVerbRecord();
all_verbs[Verb::kKill] = GetKillVerbRecord();
diff --git a/src/developer/debug/zxdb/console/verbs.h b/src/developer/debug/zxdb/console/verbs.h
index 2acd33f..2d80e98 100644
--- a/src/developer/debug/zxdb/console/verbs.h
+++ b/src/developer/debug/zxdb/console/verbs.h
@@ -54,6 +54,7 @@
kEnable,
kFinish,
kGet,
+ kHandle,
kHelp,
kJump,
kKill,