Skip to content

Commit

Permalink
Merge branch 'refs/heads/upstream-HEAD' into repo-HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
Delphix Engineering committed Aug 10, 2023
2 parents 1570ae5 + 32c7cae commit b49cdf4
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 15 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ and this project adheres to
- [#2696](https://github.com/iovisor/bpftrace/pull/2696)
- Improve listing and 'probe' builtin for several probe types
- [#2691](https://github.com/iovisor/bpftrace/pull/2691)
- Allow probe builtin with aliased software/hardware probes
- [#2711](https://github.com/iovisor/bpftrace/pull/2711)


## [0.18.0] 2023-05-15

Expand Down
41 changes: 40 additions & 1 deletion src/format_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,45 @@
#include "log.h"
#include "struct.h"

#include <unordered_map>

namespace bpftrace {

const int FMT_BUF_SZ = 512;
// bpf_trace_printk cannot use more than three arguments, see bpf-helpers(7).
const int PRINTK_MAX_ARGS = 3;

namespace {

const std::regex length_modifier_re("%-?[0-9.]*(hh|h|l|ll|j|z|t)?([cduoxXp])");
const std::unordered_map<std::string, ArgumentType> length_modifier_type = {
{ "hh", ArgumentType::CHAR }, { "h", ArgumentType::SHORT },
{ "", ArgumentType::INT }, { "l", ArgumentType::LONG },
{ "ll", ArgumentType::LONG_LONG }, { "j", ArgumentType::INTMAX_T },
{ "z", ArgumentType::SIZE_T }, { "t", ArgumentType::PTRDIFF_T },
};

ArgumentType get_expected_argument_type(const std::string &fmt)
{
std::smatch match;

if (std::regex_search(fmt, match, length_modifier_re))
{
if (match[2] == "p")
return ArgumentType::POINTER;
else if (match[2] == "c")
return ArgumentType::CHAR;

auto it = length_modifier_type.find(match[1]);
if (it != length_modifier_type.end())
return it->second;
}

return ArgumentType::UNKNOWN;
}

} // anonymous namespace

std::string validate_format_string(const std::string &fmt,
std::vector<Field> args,
const std::string call_func)
Expand Down Expand Up @@ -116,6 +149,11 @@ void FormatString::format(std::ostream &out,
if (parts_.size() < 1)
{
split();

// figure out the argument type for each format specifier
expected_types_.resize(parts_.size());
for (size_t i = 0; i < parts_.size(); i++)
expected_types_[i] = get_expected_argument_type(parts_[i]);
}
auto buffer = std::vector<char>(FMT_BUF_SZ);
auto check_snprintf_ret = [](int r) {
Expand Down Expand Up @@ -158,7 +196,8 @@ void FormatString::format(std::ostream &out,
}
int r = args.at(i)->print(buffer.data(),
buffer.capacity(),
printf_fmt.c_str());
printf_fmt.c_str(),
expected_types_[i]);
check_snprintf_ret(r);
if (static_cast<size_t>(r) < buffer.capacity())
// string fits into buffer, we are done
Expand Down
1 change: 1 addition & 0 deletions src/format_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class FormatString
private:
std::string fmt_;
std::vector<std::string> parts_;
std::vector<ArgumentType> expected_types_;

friend class cereal::access;

Expand Down
85 changes: 78 additions & 7 deletions src/printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "printf_format_types.h"
#include "struct.h"

#include <cstdint>

namespace bpftrace {

PrintableString::PrintableString(std::string value,
Expand All @@ -19,12 +21,18 @@ PrintableString::PrintableString(std::string value,
value_ += trunc_trailer;
}

int PrintableString::print(char *buf, size_t size, const char *fmt)
int PrintableString::print(char *buf,
size_t size,
const char *fmt,
ArgumentType)
{
return snprintf(buf, size, fmt, value_.c_str());
}

int PrintableBuffer::print(char *buf, size_t size, const char *fmt)
int PrintableBuffer::print(char *buf,
size_t size,
const char *fmt,
ArgumentType)
{
return snprintf(
buf,
Expand All @@ -44,18 +52,81 @@ void PrintableBuffer::escape_hex(bool value)
escape_hex_ = value;
}

int PrintableCString::print(char *buf, size_t size, const char *fmt)
int PrintableCString::print(char *buf,
size_t size,
const char *fmt,
ArgumentType)
{
return snprintf(buf, size, fmt, value_);
}

int PrintableInt::print(char *buf, size_t size, const char *fmt)
int PrintableInt::print(char *buf,
size_t size,
const char *fmt,
ArgumentType expected_type)
{
return snprintf(buf, size, fmt, value_);
// Since the value is internally always stored as a 64-bit integer, a cast is
// needed to ensure that the type of the argument passed to snprintf matches
// the format specifier.
// For example, an int64_t argument may be pushed onto the stack while an int
// is stored in a register, in which case "%d" would print the wrong value if
// we used value_ without an explicit cast.
switch (expected_type)
{
case ArgumentType::CHAR:
return snprintf(buf, size, fmt, static_cast<unsigned char>(value_));
case ArgumentType::SHORT:
return snprintf(buf, size, fmt, static_cast<unsigned short>(value_));
case ArgumentType::INT:
return snprintf(buf, size, fmt, static_cast<unsigned int>(value_));
case ArgumentType::LONG:
return snprintf(buf, size, fmt, static_cast<unsigned long>(value_));
case ArgumentType::LONG_LONG:
return snprintf(buf, size, fmt, static_cast<unsigned long long>(value_));
case ArgumentType::INTMAX_T:
return snprintf(buf, size, fmt, static_cast<uintmax_t>(value_));
case ArgumentType::SIZE_T:
return snprintf(buf, size, fmt, static_cast<size_t>(value_));
case ArgumentType::PTRDIFF_T:
return snprintf(buf, size, fmt, static_cast<ptrdiff_t>(value_));
case ArgumentType::POINTER:
return snprintf(buf, size, fmt, reinterpret_cast<void *>(value_));
case ArgumentType::UNKNOWN:
return snprintf(buf, size, fmt, value_);
}

__builtin_unreachable();
}

int PrintableSInt::print(char *buf, size_t size, const char *fmt)
int PrintableSInt::print(char *buf,
size_t size,
const char *fmt,
ArgumentType expected_type)
{
return snprintf(buf, size, fmt, value_);
switch (expected_type)
{
case ArgumentType::CHAR:
return snprintf(buf, size, fmt, static_cast<char>(value_));
case ArgumentType::SHORT:
return snprintf(buf, size, fmt, static_cast<short>(value_));
case ArgumentType::INT:
return snprintf(buf, size, fmt, static_cast<int>(value_));
case ArgumentType::LONG:
return snprintf(buf, size, fmt, static_cast<long>(value_));
case ArgumentType::LONG_LONG:
return snprintf(buf, size, fmt, static_cast<long long>(value_));
case ArgumentType::INTMAX_T:
return snprintf(buf, size, fmt, static_cast<intmax_t>(value_));
case ArgumentType::SIZE_T:
return snprintf(buf, size, fmt, static_cast<ssize_t>(value_));
case ArgumentType::PTRDIFF_T:
return snprintf(buf, size, fmt, static_cast<ptrdiff_t>(value_));
case ArgumentType::POINTER:
return snprintf(buf, size, fmt, reinterpret_cast<void *>(value_));
case ArgumentType::UNKNOWN:
return snprintf(buf, size, fmt, value_);
}

__builtin_unreachable();
}
} // namespace bpftrace
35 changes: 29 additions & 6 deletions src/printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,28 @@ const std::regex format_specifier_re(generate_pattern_string());

struct Field;

enum class ArgumentType
{
UNKNOWN = 0,
CHAR,
SHORT,
INT,
LONG,
LONG_LONG,
INTMAX_T,
SIZE_T,
PTRDIFF_T,
POINTER,
};

class IPrintable
{
public:
virtual ~IPrintable() { };
virtual int print(char* buf, size_t size, const char* fmt) = 0;
virtual int print(char* buf,
size_t size,
const char* fmt,
ArgumentType expected_type = ArgumentType::UNKNOWN) = 0;
};

class PrintableString : public virtual IPrintable
Expand All @@ -39,7 +56,7 @@ class PrintableString : public virtual IPrintable
PrintableString(std::string value,
std::optional<size_t> buffer_size = std::nullopt,
const char* trunc_trailer = nullptr);
int print(char* buf, size_t size, const char* fmt) override;
int print(char* buf, size_t size, const char* fmt, ArgumentType) override;

private:
std::string value_;
Expand All @@ -52,7 +69,7 @@ class PrintableBuffer : public virtual IPrintable
: value_(std::vector<char>(buffer, buffer + size))
{
}
int print(char* buf, size_t size, const char* fmt) override;
int print(char* buf, size_t size, const char* fmt, ArgumentType) override;
void keep_ascii(bool value);
void escape_hex(bool value);

Expand All @@ -66,7 +83,7 @@ class PrintableCString : public virtual IPrintable
{
public:
PrintableCString(char* value) : value_(value) { }
int print(char* buf, size_t size, const char* fmt) override;
int print(char* buf, size_t size, const char* fmt, ArgumentType) override;

private:
char* value_;
Expand All @@ -76,7 +93,10 @@ class PrintableInt : public virtual IPrintable
{
public:
PrintableInt(uint64_t value) : value_(value) { }
int print(char* buf, size_t size, const char* fmt) override;
int print(char* buf,
size_t size,
const char* fmt,
ArgumentType expected_type) override;

private:
uint64_t value_;
Expand All @@ -88,7 +108,10 @@ class PrintableSInt : public virtual IPrintable
PrintableSInt(int64_t value) : value_(value)
{
}
int print(char* buf, size_t size, const char* fmt) override;
int print(char* buf,
size_t size,
const char* fmt,
ArgumentType expected_type) override;

private:
int64_t value_;
Expand Down
4 changes: 4 additions & 0 deletions src/probe_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,11 @@ std::unique_ptr<std::istream> ProbeMatcher::get_symbols_from_list(
{
std::string symbols;
for (auto& probe : probes_list)
{
symbols += probe.path + ":\n";
if (!probe.alias.empty())
symbols += probe.alias + ":\n";
}
return std::make_unique<std::istringstream>(symbols);
}

Expand Down
10 changes: 10 additions & 0 deletions tests/runtime/call
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ PROG BEGIN { printf("%dst: %sa; %dnd: %sb;; %drd: %sc;;; %dth: %sd;;;;\\n", 1, "
EXPECT 1st: aa; 2nd: abb;; 3rd: abcc;;; 4th: abcdd;;;;
TIMEOUT 5

NAME printf_length_modifiers
PROG BEGIN { $x = 0x12345678abcdef; printf("%hhx %hx %x %jx\n", $x, $x, $x, $x); exit(); }
EXPECT ef cdef 78abcdef 12345678abcdef
TIMEOUT 5

NAME printf_char
PROG BEGIN { printf("%c%c%c%c\n", 0x41, 0x42, 0x43, 0x44); exit(); }
EXPECT ABCD
TIMEOUT 5

NAME time
PROG i:ms:1 { time("%H:%M:%S\n"); exit();}
EXPECT [0-9]*:[0-9]*:[0-9]*
Expand Down
3 changes: 2 additions & 1 deletion tests/runtime/engine/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ def run_test(test):

if test.cleanup:
try:
cleanup = subprocess.run(test.cleanup, shell=True, capture_output=True, text=True)
cleanup = subprocess.run(test.cleanup, shell=True, stderr=subprocess.PIPE,
stdout=subprocess.PIPE, universal_newlines=True)
cleanup.check_returncode()
except subprocess.CalledProcessError as e:
print(fail("[ FAILED ] ") + "%s.%s" % (test.suite, test.name))
Expand Down
5 changes: 5 additions & 0 deletions tests/runtime/probe
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,11 @@ PROG software:page-faults:1 { print(probe); exit();}
EXPECT software:page-faults:1
TIMEOUT 5

NAME software_alias_probe_builtin
PROG software:cpu:1 { print(probe); exit();}
EXPECT software:cpu:1
TIMEOUT 5

NAME hardware
REQUIRES ls /sys/devices/cpu/events/cache-misses
PROG hardware:cache-misses:10 { @[pid] = count(); exit(); }
Expand Down

0 comments on commit b49cdf4

Please sign in to comment.