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

Make perf ring buffer size configurable #997

Merged
merged 1 commit into from
Feb 28, 2017
Merged

Make perf ring buffer size configurable #997

merged 1 commit into from
Feb 28, 2017

Conversation

markdrayton
Copy link
Contributor

@markdrayton markdrayton commented Feb 21, 2017

As discussed in #966, this PR makes the size of the ring buffer used to send
data to userspace configurable. It changes the Python and C APIs to expose this
knob. It doesn't change the Lua API as I couldn't work out a clean way to
handle an optional parameter in the way that Python does (cc @vmg).

It also defaults the buffer size to a larger value (64 pages per CPU, an 8x
increase) for several tools which produce a lot of output, as well as making it
configurable in trace via a -b flag.

I didn't go for passing an argument for buffer size to the BPF constructor because there's already quite a few parameters there (cflags, usdt_contexts etc) and the size of the buffer seems more specific to open_perf_buffer() than a BPF context. If that approach or adding a setter function like bpf.set_perf_buffer_page_cnt() is preferable let me know.

@4ast
Copy link
Member

4ast commented Feb 22, 2017

lgtm. thanks!

@brendangregg
Copy link
Member

[buildbot, ok to test]

I should add this to cpuunclaimed, too (which sleeps during the kpoll loop to reduce overhead, which fills the buffers).

@markdrayton
Copy link
Contributor Author

@brendangregg I can add cpuunclaimed. Do you want 64 pages or more? Or -b?

@brendangregg
Copy link
Member

Taking a look at others...

I'd take it off execsnoop (doubt it needs it, unless you think otherwise). The potentially high buffer output tools where it could be added are: biosnoop, cpuunclaimed, dcsnoop, the *slower.py tools (especially when run with an arg of "0"), statsnoop, tcplife. If you want, go ahead and add it to them, thanks.

I don't have an opinion yet on what they should all be. I guess we can start with 64 pages.

I'd still avoid the -b unless we really need to, like with trace.

@markdrayton
Copy link
Contributor Author

markdrayton commented Feb 22, 2017 via email

@markdrayton
Copy link
Contributor Author

markdrayton commented Feb 22, 2017 via email

@vmg
Copy link
Contributor

vmg commented Feb 22, 2017

Hi @markdrayton! All function arguments are optional in Lua, and set to null if not passed in. So in order to emulate the Python API, all we have to do is:

function PerfEventArray:_open_perf_buffer(cpu, callback, ctype, pages_per_buffer)
  -- [...]
  -- default to `pages_per_buffer`, or 8 if not given
  local reader = libbcc.bpf_open_perf_buffer(_cb, nil, -1, cpu, pages_per_buffer or 8)

  -- [...]

@markdrayton
Copy link
Contributor Author

I think I also figured out the Lua side. Update coming.

@markdrayton
Copy link
Contributor Author

I couldn't work out the Lua stuff :-( @vmg, the problem is not with _perf_buffer_open, but perf_buffer_open, because it has some tricky vararg magic:

function PerfEventArray:open_perf_buffer(callback, data_type, ...)

I don't see how I can put an optional arg either side of the ... without confusing Lua. Based on http://stackoverflow.com/questions/6022519/define-default-values-for-function-arguments, I also tried approximating named parameters:

function PerfEventArray:open_perf_buffer(args, ...)
  setmetatable(args, {__index={page_cnt=8}})
  local callback = args[1]
  local data_type = args[2]
  assert(data_type, "a data type is needed for callback conversion")
  local ctype = ffi.typeof(data_type.."*", ...)
  for i = 0, Posix.cpu_count() - 1 do
    self:_open_perf_buffer(i, callback, ctype, args.page_cnt)
  end
end

but that fails like so:

[ERROR] bcc.lua:2756: wrong number of type parameters
stack traceback:
        [C]: in function 'typeof'
        bcc.lua:2756: in function 'open_perf_buffer'
        [..]

@vmg, any suggestions here?

@brendangregg in the meantime I have updated the other scripts you mentioned.

@markdrayton markdrayton deleted the perf-buffer-size branch February 23, 2017 06:37
@markdrayton markdrayton restored the perf-buffer-size branch February 24, 2017 18:34
@markdrayton
Copy link
Contributor Author

Apparently I did something stupid here and closed the PR.

@markdrayton markdrayton reopened this Feb 24, 2017
@brendangregg
Copy link
Member

I was wondering!

@vmg
Copy link
Contributor

vmg commented Feb 27, 2017

@markdrayton I understand now. Try this on for size:

function PerfEventArray:open_perf_buffer(callback, data_type, data_params, buffer_size)
  assert(data_type, "a data type is needed for callback conversion")
  local ctype = ffi.typeof(data_type.."*", unpack(data_params or {}))
  for i = 0, Posix.cpu_count() - 1 do
    self:_open_perf_buffer(i, callback, ctype, buffer_size)
  end
end

Here's what's what going on: the varargs to open_perf_buffers are actually optional values for the parameterized types used to build the native FFI type (see http://luajit.org/ext_ffi_semantics.html, section "Parameterized Types"). They allow you to safely template the given C type, and they are passed verbatim to ffi.typeof to do the templating.

If we want to add more optional arguments to this API, our only option is to actually pass in the data params in a table, and add the buffer_size argument after that table. Since we're now passing the types as a single table, we need to "splat" them (or unpack in Lua parlance) so ffi.typeof can receive those verbatim.

Here's how the API was used before this change:

tbl:open_perf_buffer(my_callback, "struct kernel_type { uint8_t buffer[$]; }", SIZE_IN_KERNEL)
tbl:open_perf_buffer(my_callback, "struct custom_type { $ buffer[$]; }", "uint8_t", 1024)

Notice how the templating is type-safe. In this example we're using it to reproduce a type from the kernel and "grab" the SIZE_IN_KERNEL constant in case it changes between kernel versions. It can also be used to typedef an arbitrary type.

And here's how you'd use the API now:

tbl:open_perf_buffer(my_callback, "struct kernel_type { uint8_t buffer[$]; }", {SIZE_IN_KERNEL}, 4096)
tbl:open_perf_buffer(my_callback, "struct custom_type { $ buffer[$]; }", {"uint8_t", 1024}, 4096)
tbl:open_perf_buffer(my_callback, "struct non_templated_type { int foo; }", nil, 4096)

Here we're passing in a buffer_size of 4096, besides the template arguments. That means that the template arguments must always be stored in an array/table, even for the case of a single template element. You also must pass in nil (or {}) in the case where there are no template elements and you want to pass a custom buffer size.

So yes, this is a breaking change. But I think it makes the API more consistent and more understandable. The old varargs were confusing, particularly since they were not documented.

One last thing to note: we're passing buffer_size verbatim to _open_perf_buffer. We always want to do that -- we want the nil to trickle down as much as we can before we or DEFAULT_VALUE it, to ensure that the default value is always set in a single place (in this case, the internal _open_perf_buffer.

Hope that clears things up and makes the build green. Let me know if I can help with anything else!

As discussed in #966, this PR makes the size of the ring buffer used to send
data to userspace configurable. It changes the Python, Lua and C++ APIs to
expose this knob.

It also defaults the buffer size to a larger value (64 pages per CPU, an 8x
increase) for several tools which produce a lot of output, as well as making it
configurable in `trace` via a `-b` flag.
@markdrayton
Copy link
Contributor Author

@vmg hooray! That seems to work nicely. I just updated the Lua API and any Lua scripts that call perf_buffer_open then force pushed.

@4ast, @brendangregg: this should be good to go now.

re: the red tests: It looks like something's up with the fc25 buildbot — the errors seem to relate to basic git operations and not to the code itself:

https://buildbot.iovisor.org/jenkins/job/bcc-pr/label=fc25/94/console

@vmg
Copy link
Contributor

vmg commented Feb 28, 2017

🎉👌

@4ast
Copy link
Member

4ast commented Feb 28, 2017

[buildbot, retest this please]

@drzaeus77
Copy link
Collaborator

[buildbot, test this please]

@vitalyvch
Copy link

Excellent!

@brendangregg
Copy link
Member

Revisiting this: We'd rather fix this in bcc if possible (#1033) rather than each tool, but there may be exceptions. In other words, if you are about to copy-n-paste this into another tool, please read #1033 instead.

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.

6 participants