Skip to content

Commit

Permalink
print: Print single map values
Browse files Browse the repository at this point in the history
Allows indexing a map with key(s) to print single map values while
using the print() function.
  • Loading branch information
arthurshau committed Mar 1, 2024
1 parent 9f7714f commit 9f695ea
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and this project adheres to
- [#2958](https://github.com/bpftrace/bpftrace/pull/2958)
- Add ability to list all probes in a program
- [#2969](https://github.com/bpftrace/bpftrace/pull/2969)
- Add ability to call print() with indexed maps to print single map values
- [#3027](https://github.com/bpftrace/bpftrace/pull/3027)
#### Changed
#### Deprecated
#### Removed
Expand Down
11 changes: 8 additions & 3 deletions src/ast/passes/codegen_llvm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -983,9 +983,14 @@ void CodegenLLVM::visit(Call &call)
b_.GetInsertBlock()->getParent());
b_.SetInsertPoint(deadcode);
} else if (call.func == "print") {
if (call.vargs->at(0)->is_map)
createPrintMapCall(call);
else
if (call.vargs->at(0)->is_map) {
auto &arg = *call.vargs->at(0);
auto &map = static_cast<Map &>(arg);
if (!map.vargs)
createPrintMapCall(call);
else
createPrintNonMapCall(call, non_map_print_id_);
} else
createPrintNonMapCall(call, non_map_print_id_);
} else if (call.func == "cgroup_path") {
auto elements = AsyncEvent::CgroupPath().asLLVMType(b_);
Expand Down
5 changes: 5 additions & 0 deletions src/ast/passes/resource_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,11 @@ void ResourceAnalyser::visit(Call &call)
auto &arg = *call.vargs->at(0);
if (!arg.is_map)
resources_.non_map_print_args.push_back(arg.type);
else {
auto &map = static_cast<Map &>(arg);
if (map.vargs)
resources_.non_map_print_args.push_back(map.type);
}
} else if (call.func == "kstack" || call.func == "ustack") {
resources_.stackid_maps.insert(call.type.stack_type);
} else if (call.func == "cgroup_path") {
Expand Down
13 changes: 8 additions & 5 deletions src/ast/passes/semantic_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,11 @@ void SemanticAnalyser::visit(Call &call)
auto &expr = *(*call.vargs)[i];
func_arg_idx_ = i;

if (expr.is_map && skip_key_validation(call)) {
if (expr.is_map) {
Map &map = static_cast<Map &>(expr);
map.skip_key_validation = true;
// If the map is indexed, don't skip key validation
if (!map.vargs && skip_key_validation(call))
map.skip_key_validation = true;
}

expr.accept(*this);
Expand Down Expand Up @@ -930,9 +932,10 @@ void SemanticAnalyser::visit(Call &call)
if (arg.is_map) {
Map &map = static_cast<Map &>(arg);
if (map.vargs != nullptr) {
LOG(ERROR, call.loc, err_)
<< "The map passed to " << call.func << "() should not be "
<< "indexed by a key";
if (call.vargs->size() != 1)
LOG(ERROR, call.loc, err_)
<< "Single-value (i.e. indexed) map print cannot take top "
"or div arguments.";
}

if (is_final_pass()) {
Expand Down
10 changes: 10 additions & 0 deletions tests/runtime/call
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,16 @@ REQUIRES_FEATURE kfunc
TIMEOUT 5
AFTER ./testprogs/syscall open

NAME print_map_item
PROG BEGIN { @x[1] = 5; print(@x[1]); exit() }
EXPECT 5
TIMEOUT 1

NAME print_map_item_tuple
PROG BEGIN { @x[1] = "hi"; print((1, 2, @x[1])); exit() }
EXPECT (1, 2, hi)
TIMEOUT 1

NAME strftime
PROG BEGIN { $ts = strftime("%m/%d/%y", nsecs); printf("%s\n", $ts); exit(); }
EXPECT_REGEX [0-9]{2}\/[0-9]{2}\/[0-9]{2}
Expand Down
26 changes: 25 additions & 1 deletion tests/semantic_analyser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,6 @@ TEST(semantic_analyser, call_print)

test("kprobe:f { print(@x); @x[1,2] = count(); }", 0);
test("kprobe:f { @x[1,2] = count(); print(@x); }", 0);
test("kprobe:f { @x[1,2] = count(); print(@x[3,4]); }", 1);

test("kprobe:f { @x = count(); @ = print(@x); }", 1);
test("kprobe:f { @x = count(); $y = print(@x); }", 1);
Expand All @@ -583,6 +582,31 @@ TEST(semantic_analyser, call_print)
"top and div arguments are ignored");
}

TEST(semantic_analyser, call_print_map_item)
{
test(R"_(BEGIN { @x[1] = 1; print(@x[1]); })_", 0);
test(R"_(BEGIN { @x[1] = 1; @x[2] = 2; print(@x[2]); })_", 0);
test(R"_(BEGIN { @x[1] = 1; print(@x[2]); })_", 0);
test(R"_(BEGIN { @x[3, 5] = 1; print(@x[3, 5]); })_", 0);
test(R"_(BEGIN { @x[1,2] = "asdf"; print((1, 2, @x[1,2])); })_", 0);

test_error("BEGIN { @x[1] = 1; print(@x[\"asdf\"]); }", R"(
stdin:1:20-36: ERROR: Argument mismatch for @x: trying to access with arguments: [string[5]] when map expects arguments: [unsigned int64]
BEGIN { @x[1] = 1; print(@x["asdf"]); }
~~~~~~~~~~~~~~~~
)");
test_error("BEGIN { print(@x[2]); }", R"(
stdin:1:9-20: ERROR: Undefined map: @x
BEGIN { print(@x[2]); }
~~~~~~~~~~~
)");
test_error("BEGIN { @x[1] = 1; print(@x[1], 10, 3); }", R"(
stdin:1:20-39: ERROR: Single-value (i.e. indexed) map print cannot take top or div arguments.
BEGIN { @x[1] = 1; print(@x[1], 10, 3); }
~~~~~~~~~~~~~~~~~~~
)");
}

TEST(semantic_analyser, call_print_non_map)
{
test(R"_(BEGIN { print(1) })_", 0);
Expand Down

0 comments on commit 9f695ea

Please sign in to comment.