Skip to content

Commit

Permalink
[BACKPORT 2024.1][#22841] docdb: Add sorting to stack trace tracking …
Browse files Browse the repository at this point in the history
…endpoint

Summary:
Original commit: 7f9d086 / D35795
This diff converts stack trace tracking UI endpoints to using HtmlPrintHelper to print the tables,
which has per-column sorting.

Changes were also made to optimize the sorting script, support sorting the output of
`HumanReadableNumBytes::ToString`, and to sort by string when there are a mix of string/number
values.

{image uri=https://github.com/yugabyte/yugabyte-db/assets/6742815/96e204f1-3392-4d47-8638-33d184d6a0f5, alt=screenshot of /io-stack-traces}
Jira: DB-11738

Test Plan:
Checked /io-stack-traces and /debug-stack-traces on local cluster and ensured sorting
works correctly for each column (strings, numbers, bytes).

Also checked with manually edited table that sort works as expected for:
- mix of string/number (sort by string)
- number + non-byte suffix (treated as string)
- non-number + byte suffix (treated as string)

Reviewers: hsunder

Reviewed By: hsunder

Subscribers: ybase

Tags: #jenkins-ready

Differential Revision: https://phorge.dev.yugabyte.com/D35911
  • Loading branch information
es1024 committed Jun 26, 2024
1 parent aa41e2b commit 410f110
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 79 deletions.
59 changes: 26 additions & 33 deletions src/yb/server/default-path-handlers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@
#include "yb/gutil/strings/human_readable.h"
#include "yb/gutil/strings/split.h"
#include "yb/gutil/strings/substitute.h"
#include "yb/rpc/secure.h"
#include "yb/rpc/secure_stream.h"
#include "yb/server/html_print_helper.h"
#include "yb/server/pprof-path-handlers.h"
#include "yb/server/server_base.h"
#include "yb/rpc/secure.h"
#include "yb/server/webserver.h"
#include "yb/util/flags.h"
#include "yb/util/format.h"
Expand Down Expand Up @@ -665,79 +666,71 @@ static void HandleGetVersionInfo(
}

static void IOStackTraceHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
std::stringstream *output = &resp->output;
std::stringstream& output = resp->output;

if (!GetAtomicFlag(&FLAGS_track_stack_traces)) {
(*output) << "track_stack_traces must be turned on to use this page.";
output << "track_stack_traces must be turned on to use this page.";
return;
}

Tags tags(false /* as_text */);
HtmlPrintHelper html_print_helper(output);

auto traces = GetTrackedStackTraces();
std::sort(traces.begin(), traces.end(),
[](const auto& left, const auto& right) { return left.weight > right.weight; });

(*output) << tags.header << "I/O stack traces" << tags.end_header;
output << tags.header << "I/O stack traces" << tags.end_header;

auto stack_traces = html_print_helper.CreateTablePrinter(
"stack_traces", {"Type", "Count", "Bytes", "Stack Trace"});

(*output) << tags.table << tags.row
<< tags.table_header << "Type" << tags.end_table_header
<< tags.table_header << "Count" << tags.end_table_header
<< tags.table_header << "Bytes" << tags.end_table_header
<< tags.table_header << "Stack Trace" << tags.end_table_header
<< tags.end_row;
for (const auto& entry : traces) {
if (entry.count == 0 ||
(entry.group != StackTraceTrackingGroup::kReadIO &&
entry.group != StackTraceTrackingGroup::kWriteIO)) {
continue;
}
(*output) << tags.row
<< tags.cell
<< (entry.group == StackTraceTrackingGroup::kReadIO ? "Read" : "Write")
<< tags.end_cell
<< tags.cell << entry.count << tags.end_cell
<< tags.cell << HumanReadableNumBytes::ToString(entry.weight) << tags.end_cell
<< tags.cell << tags.pre_tag << EscapeForHtmlToString(entry.symbolized_trace)
<< tags.end_pre_tag << tags.end_cell
<< tags.end_row;
stack_traces.AddRow(
entry.group == StackTraceTrackingGroup::kReadIO ? "Read" : "Write",
entry.count,
HumanReadableNumBytes::ToString(entry.weight),
tags.pre_tag + EscapeForHtmlToString(entry.symbolized_trace) + tags.end_pre_tag);
}

(*output) << tags.end_table;
stack_traces.Print();
}

static void DebugStackTraceHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
std::stringstream *output = &resp->output;
std::stringstream& output = resp->output;

if (!GetAtomicFlag(&FLAGS_track_stack_traces)) {
(*output) << "track_stack_traces must be turned on to use this page.";
output << "track_stack_traces must be turned on to use this page.";
return;
}

Tags tags(false /* as_text */);
HtmlPrintHelper html_print_helper(output);

auto traces = GetTrackedStackTraces();
std::sort(traces.begin(), traces.end(),
[](const auto& left, const auto& right) { return left.count > right.count; });

(*output) << tags.header << "Tracked stack traces" << tags.end_header;
output << tags.header << "Tracked stack traces" << tags.end_header;

auto stack_traces = html_print_helper.CreateTablePrinter(
"stack_traces", {"Count", "Stack Trace"});

(*output) << tags.table << tags.row
<< tags.table_header << "Count" << tags.end_table_header
<< tags.table_header << "Stack Trace" << tags.end_table_header
<< tags.end_row;
for (const auto& entry : traces) {
if (entry.count == 0 || entry.group != StackTraceTrackingGroup::kDebugging) {
continue;
}
(*output) << tags.row
<< tags.cell << entry.count << tags.end_cell
<< tags.cell << tags.pre_tag << EscapeForHtmlToString(entry.symbolized_trace)
<< tags.end_pre_tag << tags.end_cell
<< tags.end_row;
stack_traces.AddRow(
entry.count,
tags.pre_tag + EscapeForHtmlToString(entry.symbolized_trace) + tags.end_pre_tag);
}

(*output) << tags.end_table;
stack_traces.Print();
}

static void ResetStackTraceHandler(const Webserver::WebRequest& req, Webserver::WebResponse* resp) {
Expand Down
109 changes: 63 additions & 46 deletions src/yb/server/html_print_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,66 +23,83 @@ namespace {
const char* const kSortAndFilterTableScript = R"(
<script>
function castIfNumber(elem) {
return elem.length ?
(elem.length > 14 || isNaN(Number(elem)) ?
elem.toLowerCase() :
Number(elem)) :
"~";
const byte_units = "BKMGTPE";
// Put empty strings at end of number column.
if (!elem.length) {
return Infinity;
}
if (elem.length <= 15) {
const num = Number(elem);
if (!isNaN(num)) {
return num;
}
// Sorting for HumanReadableNumBytes::ToString.
const byte_unit = byte_units.indexOf(elem[elem.length - 1]);
if (byte_unit !== -1) {
const byte_num = Number(elem.substring(0, elem.length - 1));
if (!isNaN(byte_num)) {
return byte_num * Math.pow(2, 10 * byte_unit);
}
}
}
return elem;
}
function normalizeString(elem) {
return elem.toLowerCase();
}
function sortTable(table_id, n) {
var asc_symb = ' <span style="color: grey">\u25B2</span>';
var desc_symb = ' <span style="color: grey">\u25BC</span>';
var i, swapCount = 0;
var table = document.getElementById(table_id);
const asc_symb = ' <span style="color: grey">\u25B2</span>';
const desc_symb = ' <span style="color: grey">\u25BC</span>';
const table = document.getElementById(table_id);
if (table.rows.length < 3) {
return;
}
var switching = true;
var asc = true;
if (table.rows[0].getElementsByTagName("TH")[n].innerHTML.includes(asc_symb)) {
asc = false;
}
for(var j = 0; j < table.rows[0].getElementsByTagName("TH").length; j++) {
table.rows[0].getElementsByTagName("TH")[j].innerHTML =
table.rows[0].getElementsByTagName("TH")[j].innerHTML.replace(asc_symb, "").replace(desc_symb,
"");
const tbody = table.tBodies[0];
const rows = new Array(...tbody.children);
const header_row = rows.shift();
const asc = !header_row.getElementsByTagName("TH")[n].innerHTML.includes(asc_symb);
for (let j = 0; j < header_row.children.length; ++j) {
const header = header_row.children[j];
let contents = header.innerHTML;
contents = contents.replace(asc_symb, "").replace(desc_symb, "");
if (j == n) {
sort_symb = asc ? asc_symb : desc_symb;
table.rows[0].getElementsByTagName("TH")[j].innerHTML =
table.rows[0].getElementsByTagName("TH")[j].innerHTML.concat(sort_symb);
contents += sort_symb;
}
header.innerHTML = contents;
}
while (switching) {
switching = false;
// Ignore header row.
for (i = 1; i < (table.rows.length - 1); i++) {
var swap = false;
var x = table.rows[i].getElementsByTagName("TD")[n];
var y = table.rows[i + 1].getElementsByTagName("TD")[n];
var cmpX = castIfNumber(x.innerHTML);
var cmpY = castIfNumber(y.innerHTML);
if (asc) {
if (cmpX > cmpY) {
swap= true;
break;
}
} else {
if (cmpX < cmpY) {
swap = true;
break;
}
}
}
let all_number = true;
for (const row of rows) {
all_number = all_number && typeof castIfNumber(row.children[n].innerText) == 'number';
}
if (swap) {
table.rows[i].parentNode.insertBefore(table.rows[i + 1], table.rows[i]);
switching = true;
}
if (all_number) {
rows.sort((x, y) => {
const cmpX = castIfNumber(x.children[n].innerText);
const cmpY = castIfNumber(y.children[n].innerText);
return asc ? cmpX - cmpY : cmpY - cmpX;
});
} else {
rows.sort((x, y) => {
const cmpX = normalizeString(x.children[n].innerText);
const cmpY = normalizeString(y.children[n].innerText);
return (asc ? 1 : -1) * cmpX.localeCompare(cmpY);
});
}
tbody.replaceChildren(header_row, ...rows);
}
function filterTableFunction(input_id, table_id) {
Expand Down

0 comments on commit 410f110

Please sign in to comment.