From 7c98c79573d2448f833290f55421fe8f7ea3c92a Mon Sep 17 00:00:00 2001 From: Arthur Shau Date: Fri, 1 Mar 2024 02:02:32 -0800 Subject: [PATCH] print: Print single map values Allows indexing a map with key(s) to print single map values while using the print() function. --- CHANGELOG.md | 2 ++ src/ast/passes/codegen_llvm.cpp | 11 ++++++++--- src/ast/passes/resource_analyser.cpp | 5 +++++ src/ast/passes/semantic_analyser.cpp | 13 ++++++++----- tests/runtime/call | 10 ++++++++++ tests/semantic_analyser.cpp | 26 +++++++++++++++++++++++++- 6 files changed, 58 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fa3b673..2503272f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/ast/passes/codegen_llvm.cpp b/src/ast/passes/codegen_llvm.cpp index 532e1bac..7a9cd09b 100644 --- a/src/ast/passes/codegen_llvm.cpp +++ b/src/ast/passes/codegen_llvm.cpp @@ -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(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_); diff --git a/src/ast/passes/resource_analyser.cpp b/src/ast/passes/resource_analyser.cpp index d6cf4c9e..e092b2c6 100644 --- a/src/ast/passes/resource_analyser.cpp +++ b/src/ast/passes/resource_analyser.cpp @@ -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(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") { diff --git a/src/ast/passes/semantic_analyser.cpp b/src/ast/passes/semantic_analyser.cpp index e07e331e..b5889506 100644 --- a/src/ast/passes/semantic_analyser.cpp +++ b/src/ast/passes/semantic_analyser.cpp @@ -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(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); @@ -930,9 +932,10 @@ void SemanticAnalyser::visit(Call &call) if (arg.is_map) { Map &map = static_cast(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 additional " + "arguments."; } if (is_final_pass()) { diff --git a/tests/runtime/call b/tests/runtime/call index 7f7cce6d..4b4c4a69 100644 --- a/tests/runtime/call +++ b/tests/runtime/call @@ -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} diff --git a/tests/semantic_analyser.cpp b/tests/semantic_analyser.cpp index 1d9c0e0d..1698437e 100644 --- a/tests/semantic_analyser.cpp +++ b/tests/semantic_analyser.cpp @@ -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); @@ -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], 3, 5); }", R"( +stdin:1:20-38: ERROR: Single-value (i.e. indexed) map print cannot take additional arguments. +BEGIN { @x[1] = 1; print(@x[1], 3, 5); } + ~~~~~~~~~~~~~~~~~~ +)"); +} + TEST(semantic_analyser, call_print_non_map) { test(R"_(BEGIN { print(1) })_", 0);