Skip to content

Commit 2fc1571

Browse files
Driegermmarchini
authored andcommitted
src: option to limit output of findjsinstances
As the number of instances might grow large in some applications, give the user an option to limit the number of entries displayed on command output. Next entries will be displayes when the same command is used or just by pressing [ENTER], this will repeat last command. Refs: #230 PR-URL: #235 Reviewed-By: Matheus Marchini <[email protected]>
1 parent 7b09c15 commit 2fc1571

File tree

6 files changed

+117
-7
lines changed

6 files changed

+117
-7
lines changed

src/llnode.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,8 +463,14 @@ bool PluginInitialize(SBDebugger d) {
463463

464464
v8.AddCommand("findjsinstances", new llnode::FindInstancesCmd(&llscan, false),
465465
"List every object with the specified type name.\n"
466-
"Use -v or --verbose to display detailed `v8 inspect` output "
466+
"Flags:\n\n"
467+
" * -v, --verbose - display detailed `v8 "
468+
"inspect` output "
467469
"for each object.\n"
470+
" * -n <num> --output-limit <num> - limit the number of "
471+
"entries displayed "
472+
"to `num` (use 0 to show all). To get next page repeat command "
473+
"or press [ENTER].\n"
468474
"Accepts the same options as `v8 inspect`");
469475

470476
interpreter.AddCommand("findjsinstances",

src/llscan.cc

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <sstream>
1111
#include <string>
1212
#include <vector>
13+
#include <iostream>
1314

1415
#include <lldb/API/SBExpressionOptions.h>
1516

@@ -43,6 +44,7 @@ char** ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options) {
4344
{"print-source", no_argument, nullptr, 's'},
4445
{"verbose", no_argument, nullptr, 'v'},
4546
{"detailed", no_argument, nullptr, 'd'},
47+
{"output-limit", required_argument, nullptr, 'n'},
4648
{nullptr, 0, nullptr, 0}};
4749

4850
int argc = 1;
@@ -60,7 +62,7 @@ char** ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options) {
6062
optind = 0;
6163
opterr = 1;
6264
do {
63-
int arg = getopt_long(argc, args, "Fmsdvl:", opts, nullptr);
65+
int arg = getopt_long(argc, args, "Fmsdvln:", opts, nullptr);
6466
if (arg == -1) break;
6567

6668
switch (arg) {
@@ -80,6 +82,10 @@ char** ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options) {
8082
case 'v':
8183
options->detailed = true;
8284
break;
85+
case 'n': {
86+
int limit = strtol(optarg, nullptr, 10);
87+
options->output_limit = limit && limit > 0 ? limit : 0;
88+
} break;
8389
default:
8490
continue;
8591
}
@@ -213,6 +219,7 @@ bool FindInstancesCmd::DoExecute(SBDebugger d, char** cmd,
213219

214220
inspect_options.detailed = detailed_;
215221

222+
// Use same options as inspect?
216223
char** start = ParseInspectOptions(cmd, &inspect_options);
217224

218225
std::string full_cmd;
@@ -222,15 +229,57 @@ bool FindInstancesCmd::DoExecute(SBDebugger d, char** cmd,
222229

223230
TypeRecordMap::iterator instance_it =
224231
llscan_->GetMapsToInstances().find(type_name);
232+
225233
if (instance_it != llscan_->GetMapsToInstances().end()) {
234+
235+
226236
TypeRecord* t = instance_it->second;
227-
for (std::set<uint64_t>::iterator it = t->GetInstances().begin();
228-
it != t->GetInstances().end(); ++it) {
237+
238+
// Update pagination options
239+
if (full_cmd != pagination_.command ||
240+
inspect_options.output_limit != pagination_.output_limit) {
241+
pagination_.total_entries = t->GetInstanceCount();
242+
pagination_.command = full_cmd;
243+
pagination_.current_page = 0;
244+
pagination_.output_limit = inspect_options.output_limit;
245+
} else {
246+
if (pagination_.output_limit <= 0 ||
247+
(pagination_.current_page + 1) * pagination_.output_limit >
248+
pagination_.total_entries) {
249+
pagination_.current_page = 0;
250+
} else {
251+
pagination_.current_page++;
252+
}
253+
}
254+
255+
int initial_p_offset =
256+
(pagination_.current_page * inspect_options.output_limit);
257+
int final_p_offset =
258+
initial_p_offset +
259+
std::min(pagination_.output_limit,
260+
pagination_.total_entries -
261+
pagination_.current_page * pagination_.output_limit);
262+
if (final_p_offset <= 0) {
263+
final_p_offset = pagination_.total_entries;
264+
}
265+
266+
std::set<uint64_t>::iterator it =
267+
pagination_.current_page == 0
268+
? t->GetInstances().begin()
269+
: std::next(t->GetInstances().begin(), initial_p_offset);
270+
for (; it != t->GetInstances().end() &&
271+
it != (std::next(t->GetInstances().begin(), final_p_offset));
272+
++it) {
229273
Error err;
230274
v8::Value v8_value(llscan_->v8(), *it);
231275
std::string res = v8_value.Inspect(&inspect_options, err);
232276
result.Printf("%s\n", res.c_str());
233277
}
278+
if (it != t->GetInstances().end()) {
279+
result.Printf("..........\n");
280+
}
281+
result.Printf("(Showing %d to %d of %d instances)\n", initial_p_offset + 1,
282+
final_p_offset, pagination_.total_entries);
234283

235284
} else {
236285
// "No objects found with type name %s", type_name

src/llscan.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ typedef std::map<uint64_t, ReferencesVector*> ReferencesByValueMap;
1919
typedef std::map<std::string, ReferencesVector*> ReferencesByPropertyMap;
2020
typedef std::map<std::string, ReferencesVector*> ReferencesByStringMap;
2121

22+
23+
// New type defining pagination options
24+
// It should be feasible to use it to any commands that output
25+
// a list of information
26+
struct cmd_pagination_t {
27+
int total_entries = 0;
28+
int current_page = 0;
29+
int output_limit = 0;
30+
std::string command = "";
31+
};
32+
2233
char** ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options);
2334

2435
class FindObjectsCmd : public CommandBase {
@@ -39,7 +50,8 @@ class FindObjectsCmd : public CommandBase {
3950
class FindInstancesCmd : public CommandBase {
4051
public:
4152
FindInstancesCmd(LLScan* llscan, bool detailed)
42-
: llscan_(llscan), detailed_(detailed) {}
53+
: llscan_(llscan), detailed_(detailed) {
54+
}
4355
~FindInstancesCmd() override {}
4456

4557
bool DoExecute(lldb::SBDebugger d, char** cmd,
@@ -48,6 +60,7 @@ class FindInstancesCmd : public CommandBase {
4860
private:
4961
LLScan* llscan_;
5062
bool detailed_;
63+
cmd_pagination_t pagination_;
5164
};
5265

5366
class NodeInfoCmd : public CommandBase {

src/llv8.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ class Value {
4545
print_map(false),
4646
print_source(false),
4747
length(kLength),
48-
indent_depth(1) {}
48+
indent_depth(1),
49+
output_limit(0) {}
4950

5051
static const unsigned int kLength = 16;
5152
static const unsigned int kIndentSize = 2;
@@ -58,6 +59,7 @@ class Value {
5859
bool print_source;
5960
unsigned int length;
6061
unsigned int indent_depth;
62+
int output_limit;
6163
};
6264

6365
Value(const Value& v) = default;

test/fixtures/inspect-scenario.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ function closure() {
2424
this.hashmap = {};
2525
}
2626

27+
2728
Class.prototype.method = function method() {
2829
throw new Error('Uncaught');
2930
};
@@ -76,6 +77,13 @@ function closure() {
7677
return scopedVar + outerVar + scopedAPI + scopedArray;
7778
};
7879

80+
function Class_B() {
81+
this.name = "Class B";
82+
}
83+
84+
const arr = new Array();
85+
for(let i=0; i < 10; i++) arr.push(new Class_B());
86+
7987
c.method();
8088
}
8189

test/plugin/scan-test.js

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,43 @@ function test(executable, core, t) {
4646
t.ok(/3 +0 Class: x, y, hashmap/.test(lines.join('\n')),
4747
'"Class: x, y, hashmap" should be in findjsobjects -d');
4848

49-
sess.send('v8 findjsinstances Zlib');
49+
sess.send('v8 findjsinstances Class_B')
5050
// Just a separator
5151
sess.send('version');
5252
});
5353

54+
sess.linesUntil(versionMark, (err, lines) => {
55+
t.error(err);
56+
57+
t.ok((lines.join('\n').match(/<Object: Class_B>/g)).length == 10, 'Should show 10 instances');
58+
t.ok(/\(Showing 1 to 10 of 10 instances\)/.test(lines.join('\n')), 'Should show 1 to 10 ');
59+
60+
sess.send('v8 findjsinstances -n 5 Class_B');
61+
sess.send('version');
62+
});
63+
64+
sess.linesUntil(versionMark, (err, lines) => {
65+
t.error(err);
66+
67+
t.ok((lines.join('\n').match(/<Object: Class_B>/g)).length == 5, 'Should show 5 instances');
68+
t.ok(/\.\.\.\.\.\.\.\.\.\./.test(lines.join('\n')), 'Should show that more instances are available');
69+
t.ok(/\(Showing 1 to 5 of 10 instances\)/.test(lines.join('\n')), 'Should show 1 to 5 ');
70+
71+
sess.send('v8 findjsinstances -n 5 Class_B');
72+
sess.send('version');
73+
});
74+
75+
sess.linesUntil(versionMark, (err, lines) => {
76+
t.error(err);
77+
78+
t.ok((lines.join('\n').match(/<Object: Class_B>/g)).length == 5, 'Should show 5 instances');
79+
t.notOk(/\.\.\.\.\.\.\.\.\.\./.test(lines.join('\n')), 'Should not show ellipses');
80+
t.ok(/\(Showing 6 to 10 of 10 instances\)/.test(lines.join('\n')), 'Should show 6 to 10 ');
81+
82+
sess.send('v8 findjsinstances Zlib');
83+
sess.send('version');
84+
});
85+
5486
sess.linesUntil(versionMark, (err, lines) => {
5587
t.error(err);
5688
// Find refs to every Zlib instance

0 commit comments

Comments
 (0)