Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bswap built-in function #2166

Merged
merged 1 commit into from
Apr 1, 2022
Merged

Conversation

gedimitr
Copy link
Contributor

@gedimitr gedimitr commented Mar 13, 2022

The function takes an unsigned integer and returns an unsigned
integer of the same width, but with the byte order reversed.

Resolves #1875

One thing I'd like to draw your attention is the integer type promotion / demotion in order to fix a strange behaviour when 16 bit arguments were received. Since I am not really competent in LLVM, it is possible that something is escaping me....

Also, I have updated TCP related programs (e.g. tcpconnect.bt) to use the new bswap function, instead of the manual byte reordering.

Checklist
  • Language changes are updated in man/adoc/bpftrace.adoc and if needed in docs/reference_guide.md
  • User-visible and non-trivial changes updated in CHANGELOG.md
  • The new behaviour is covered by tests

@gedimitr gedimitr force-pushed the bswap_builtin branch 3 times, most recently from abb2006 to b7df4b1 Compare March 13, 2022 20:53
@gedimitr gedimitr marked this pull request as ready for review March 13, 2022 21:18
// correct bswap intrinsic would be selected, (bswap.i16), yet LLVM would
// give to it a zero extended i64 argument! The trick below seems to fix
// this behaviour and the argument to bswap is always of the correct type.
Value *promoted_value = b_.CreateIntCast(expr_, b_.getInt64Ty(), false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this happen in the original IR or the optimized one? We have some weird casts in there for 'reasons' which can make the optimized ir confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a build without the promoting / demoting trick and compared the unoptimized / optimized IRs from tcpconnect.bt.

Unoptimized IR

  %"$dport" = alloca i16, align 2
  ...
  %75 = load i16, i16* %"struct sock_common.skc_dport", align 2
  %76 = zext i16 %75 to i64
  store i64 %76, i16* %"$dport", align 8
  %78 = load i16, i16* %"$dport", align 2
  %79 = call i16 @llvm.bswap.i16(i16 %78)

Optimized IR

  %16 = load i16, i16* %"struct sock_common.skc_dport", align 2
  %17 = zext i16 %16 to i64
  %18 = call i16 @llvm.bswap.i16(i64 %17)

In the unoptimized version the bswap intrinsic is correctly called with a i16 argument, but the storing of an i64 value into the i16 $dport looks strange...

auto scoped_del_arg = accept(arg);

assert(arg->type.IsIntegerTy());
if (arg->type.GetSize() > 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this and the assert? Isn't this guaranteed by the semantic analyser?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the assert in order to explicitly declare this expectation / guarantee from the semantic analyser, as well as a safeguard in case the behaviour of the semantic analyzer changes in the future.

Regarding the arg->type.GetSize() check, the intention is that bswap is invoked only for multibyte integers, while i8 ones are simply passed through.

Should I amend?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah skipping the byte case makes sense.

re assert, we should try to avoid them as the end user experience of asserts is quite bad. But in this case have test coverage for it (test("kprobe:f { bswap(\"hello\"); }", 10);) so we will probably catch it before it ends up in the master branch

@gedimitr
Copy link
Contributor Author

I did a more throrough investigation and I added a changeset which fixes this strange behaviour:

  • When an element was read from a compound data structure in probereadDatastructElem, the returned value would uncoditionally be cast to a 64 bit integer
  • By removing this cast, the narrower types get correctly handled
  • The promoting / demoting trick is no longer necessary

After running the runtime tests, some errors were reported for some test cases due to differences in the generated IRs. I had a look into the diffs and they appear to be benign.

I also added a fix in the semantic analysis of bswap, because assertion failures were occurring when the tests were running in debug builds.

@fbs
Copy link
Member

fbs commented Mar 24, 2022

I did a more throrough investigation and I added a changeset which fixes this strange behaviour:

* When an element was read from a compound data structure in `probereadDatastructElem`, the returned value would uncoditionally be cast to a 64 bit integer

* By removing this cast, the narrower types get correctly handled

* The promoting / demoting trick is no longer necessary

Before we merge this we should run all the tools we have and make sure the output makes sense. We had some issues in the past and these weird casts are part of it, e.g. #1173

@gedimitr
Copy link
Contributor Author

Went through all the programs in the tools directory and run them: all of them executed successfully and provided sensible results.

Exceptions (that by all accounts are irrelevant to the changes introduced with this PR):

  • I got some warnings (e.g. biolatency.bt complained that __blk_account_io_start / __blk_account_io_done is not traceable)
  • mdflush failed to start due to an error in a struct definition (ERROR: Struct/union of type 'struct bio' does not contain a field named 'bi_bdev')
  • The xfsdist.bt program wasn't tested for results accuracy, since no XFS filesystem was available (the program was successfully started and the probes established)

@viktormalik
Copy link
Contributor

Exceptions (that by all accounts are irrelevant to the changes introduced with this PR):

  • I got some warnings (e.g. biolatency.bt complained that __blk_account_io_start / __blk_account_io_done is not traceable)
  • mdflush failed to start due to an error in a struct definition (ERROR: Struct/union of type 'struct bio' does not contain a field named 'bi_bdev')
  • The xfsdist.bt program wasn't tested for results accuracy, since no XFS filesystem was available (the program was successfully started and the probes established)

I can confirm that these should be unrelated to this PR.

Copy link
Contributor

@viktormalik viktormalik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

I think that getting rid of that unconditional cast is reasonable. Looking at the changes in codegen tests, it seems that each operation does typecasts on its inputs when needed (which is the way it should be done anyway), so hopefully nothing will break.

Could you update the codegen tests so that CI passes? There's a script in scripts/ for that.

@gedimitr
Copy link
Contributor Author

Thanks @viktormalik! I have updated the codegen tests as you proposed above.

Copy link
Contributor

@viktormalik viktormalik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks! Could you squash the commits, please?

The function takes an unsigned integer and returns an unsigned
integer of the same width, but with the byte order reversed.

Changed behaviour when reading elements from compound data
structures: Previously, the returned value would unconditionally
be cast to a 64 bit integer --- this would cause LLVM intrinsic
invocations to fail if narrower integers were involved. Now,
an integer of the same width is returned as the accessed field:
it is the callers responsibility to cast to a wider integer
(something which is nevertheless currently done)
@gedimitr
Copy link
Contributor Author

@viktormalik Just pushed everything as a single comit. Thanks!

@viktormalik viktormalik requested a review from fbs March 30, 2022 05:00
@viktormalik viktormalik merged commit 1972e89 into bpftrace:master Apr 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add bswap() helper
3 participants