Skip to content

Conversation

@martinus
Copy link
Contributor

@martinus martinus commented May 7, 2022

Currently there is a macro DEBUG_LOCKCONTENTION (see developer-notes.md) to enable logging of contention. The disadvantage of this method is that bitcoind needs to be especially compiled with that enabled, and when enabled this quickly produces huge log files, and has a relatively large overhead.

This PR introduces some tracepoints and adds a tracing script lock_contention_analysis.bt that can be started at any time while bitcoind is running. This has low overhead and gives better analysis possibilities.

When the script is attached with sudo bpftrace contrib/tracing/lock_contention_analysis.bt, you'll get a markdown formatted output that looks like this:

timestamp Lock waiting duration waiting at blocked by
12:41:05.512089 m_mutex 4491µs ./checkqueue.h ( 175) ./checkqueue.h ( 78)
12:41:05.861322 cs_main 535058µs net_processing.cpp (1607) validation.cpp (2994)
12:41:06.655427 m_mutex 4012µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)
12:41:06.675967 cs_main 650117µs net_processing.cpp (1607) validation.cpp (2994)
12:41:06.894090 m_mutex 2747µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)
12:41:06.961872 cs_main 142277µs net_processing.cpp (1607) validation.cpp (2994)
12:41:07.363535 cs_main 395851µs net_processing.cpp (1607) validation.cpp (2994)
12:41:07.583226 cs_main 69281µs net_processing.cpp (1607) validation.cpp (2994)
12:41:07.746665 cs_main 157604µs net_processing.cpp (1607) validation.cpp (2335)
12:41:07.958787 m_mutex 3041µs ./checkqueue.h ( 175) ./checkqueue.h ( 78)
12:41:07.958801 m_mutex 2970µs ./checkqueue.h ( 78) ./checkqueue.h ( 175)
12:41:07.958842 m_mutex 2956µs ./checkqueue.h ( 78) ./checkqueue.h ( 175)
12:41:07.958853 m_mutex 2898µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)
12:41:07.958862 m_mutex 2820µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)
12:41:07.959201 m_mutex 3028µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)
12:41:07.959218 m_mutex 2916µs ./checkqueue.h ( 78) ./checkqueue.h ( 78)

When monitoring is stopped (press Ctrl+C), the script prints the source locations where it has waited the longest while the script was running (unfortunately there seems to be no way to format this more nicely):

Monitored from 17:12:18.513400 to 17:42:08.565786. top 50 lock contention locations (file, lineNo): µs
[...]
@locked_at_source_line[./net.h, 1233]: 334654
@locked_at_source_line[txmempool.cpp, 823]: 401472
@locked_at_source_line[net_processing.cpp, 4504]: 661670
@locked_at_source_line[net_processing.cpp, 4872]: 668911
@locked_at_source_line[txmempool.cpp, 1096]: 681892
@locked_at_source_line[net_processing.cpp, 4857]: 825963
@locked_at_source_line[net_processing.cpp, 4724]: 826719
@locked_at_source_line[net_processing.cpp, 4677]: 827335
@locked_at_source_line[net_processing.cpp, 4154]: 827972
@locked_at_source_line[net_processing.cpp, 4222]: 828863
@locked_at_source_line[net.cpp, 2329]: 831334
@locked_at_source_line[net_processing.cpp, 4217]: 831451
@locked_at_source_line[net_processing.cpp, 4231]: 839408
@locked_at_source_line[net_processing.cpp, 4198]: 846353
@locked_at_source_line[util/system.cpp, 1035]: 883117
@locked_at_source_line[net.cpp, 1426]: 1398949
@locked_at_source_line[net.cpp, 1422]: 1416004
@locked_at_source_line[net.cpp, 1611]: 1564984
@locked_at_source_line[net_processing.cpp, 4205]: 1668173
@locked_at_source_line[net_processing.cpp, 1334]: 1704239
@locked_at_source_line[validation.cpp, 5173]: 2899013

For this to work I had to add traces to UniqueLock and AnnotatedMixin, and move the implementation into the cpp file (without moving, due to inlining the maximum number of traces of 512 is quickly exhausted)

martinus added 6 commits May 7, 2022 17:52
This is a templated class, so this also adds the two needed explicit
instantiations. Why do this? having tracepoints in the header leads
to lots of traces which quickly exhaust the limit. Therefore this moves
the implementation into the .cpp file.
Stores the arguments as members so they are available in the destructor.
This will be useful later when adding tracepoints.

Note that it is remembered as a `char const*`, so this only works for
string literals where long lifetime is guaranteed. UniqueLock is only
used with the `LOCK`/`LOCK2` macro, so this is fine. Using
`std::string` would be an unnecessary overhead.
This enables tracing of locks & lock contention analysis.
Same as `UniqueLock`, moves the implementation of `AnnotatedMixin` into
the cpp file. That way we can add tracepoints to the creation and
destruction of a mutex.
This bpftrace script can be used to analyze lock contention. It shows
code locations that have to wait a long time to actually acquire a
lock, and also shows the last location that lock was held, to see
who was actually responsible for blocking.

When monitoring ends, prints the locations with the longest waiting
durations.
@jonatack
Copy link
Member

jonatack commented May 7, 2022

Interesting, would like to give this a try soon.

@0xB10C
Copy link
Contributor

0xB10C commented May 7, 2022

This looks interesting. Planning to play around with this, e.g. running this on bitcoind.observer.

I think it would be good to document these tracepoints in doc/tracing.md and, if possible, have an interface test similar to these added in e.g. #24358 (test/functional/interface_usdt_*.py).

Edit: Oh also, documentation of the bpftrace script in contrib/tracing/README.md would be nice :).

@DrahtBot
Copy link
Contributor

DrahtBot commented May 8, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK laanwj, hebasto, jb55

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #25819 (msvc, refactor: Avoid some rare compiler warnings by hebasto)
  • #25676 (sync: simplify and remove unused code from sync.h by vasild)
  • #24923 (Rework logging timer by MarcoFalke)
  • #16545 (refactor: Implement missing error checking for ArgsManager flags by ryanofsky)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@martinus
Copy link
Contributor Author

martinus commented May 8, 2022

have an interface test similar to these added in e.g. #24358 (test/functional/interface_usdt_*.py).

Oh, I didn't even know we had something like that... I guess I need to learn more about eBPF and python 😅

@jonatack
Copy link
Member

jonatack commented May 8, 2022

bitcoind needs to be especially compiled with that enabled

For info, #24757 would define it by default in debug builds (--enable-debug).

@DrahtBot DrahtBot mentioned this pull request May 8, 2022
@laanwj
Copy link
Member

laanwj commented May 9, 2022

Concept ACK, I like using tracepoints for this kind of analysis instead of our own instrumentation.

For this to work I had to add traces to UniqueLock and AnnotatedMixin, and move the implementation into the cpp file (without moving, due to inlining the maximum number of traces of 512 is quickly exhausted)

No longer inlining the locks will add some overhead to locking even in the no-tracing case. We'll need to benchmark this.

@hebasto
Copy link
Member

hebasto commented May 9, 2022

Concept ACK.

@jamesob
Copy link
Contributor

jamesob commented May 17, 2022

Hvaen't reviewed the code yet, but just ran a quick IBD bench on a "contemporary" region of the chain to get a sense of any performance impact. No significant slowdown there.

commands index

bench name command
ibd.local.range.667200.677200 bitcoind -dbcache=10000 -debug=coindb -debug=bench -listen=0 -connect=0 -addnode=127.0.0.1:8888 -prune=9999999 -printtoconsole=0 -assumevalid=000000000000000000176c192f42ad13ab159fdb20198b87e7ba3c001e47b876

#25081 vs. $mergebase (absolute)

bench name x #25081 $mergebase
ibd.local.range.667200.677200.total_secs 2 1738.2652 (± 3.4354) 1736.2869 (± 0.6808)
ibd.local.range.667200.677200.peak_rss_KiB 2 3518128.0000 (± 196.0000) 3518346.0000 (± 3050.0000)
ibd.local.range.667200.677200.cpu_kernel_secs 2 126.4900 (± 1.4500) 125.9750 (± 0.5650)
ibd.local.range.667200.677200.cpu_user_secs 2 10095.4250 (± 1.1050) 10089.9250 (± 1.4550)

#25081 vs. $mergebase (relative)

bench name x #25081 $mergebase
ibd.local.range.667200.677200.total_secs 2 1.001 1.00
ibd.local.range.667200.677200.peak_rss_KiB 2 1.000 1.00
ibd.local.range.667200.677200.cpu_kernel_secs 2 1.004 1.00
ibd.local.range.667200.677200.cpu_user_secs 2 1.001 1.00

Reproduce with

bitcoinperf bench-pr --num-blocks 10_000 --run-id lock-tracing \
  --bitcoind-args='-dbcache=10000' --run-count 2 25081

@jb55
Copy link
Contributor

jb55 commented May 17, 2022 via email

@0xB10C
Copy link
Contributor

0xB10C commented May 17, 2022

I've started playing around with these tracepoints. It seems like there are two ELF notes generated for each sync tracepoint.

Output of `readelf -n ./src/bitcoind`
...
  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: mutex_ctor
    Location: 0x00000000005dca7b, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi
  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: mutex_dtor
    Location: 0x00000000005dcab8, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi
  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: mutex_ctor
    Location: 0x00000000005dcc12, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi
  stapsdt              0x0000002f	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: mutex_dtor
    Location: 0x00000000005dcc48, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi
  stapsdt              0x0000004e	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: enter
    Location: 0x00000000005dcd8c, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@(%rdi) 8@16(%rdi) 8@24(%rdi) -4@32(%rdi)
  stapsdt              0x0000004d	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: locked
    Location: 0x00000000005dcdb5, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi 8@16(%rbx) 8@24(%rbx) -4@32(%rbx)
  stapsdt              0x00000051	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: try_locked
    Location: 0x00000000005dce5c, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi 8@16(%rbx) 8@24(%rbx) -4@32(%rbx)
  stapsdt              0x00000044	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: unlock
    Location: 0x00000000005dceae, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@(%rdi) 8@24(%rdi) -4@32(%rdi)
  stapsdt              0x0000004e	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: enter
    Location: 0x00000000005dd14c, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@(%rdi) 8@16(%rdi) 8@24(%rdi) -4@32(%rdi)
  stapsdt              0x0000004d	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: locked
    Location: 0x00000000005dd175, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi 8@16(%rbx) 8@24(%rbx) -4@32(%rbx)
  stapsdt              0x00000051	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: try_locked
    Location: 0x00000000005dd21c, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@%rdi 8@16(%rbx) 8@24(%rbx) -4@32(%rbx)
  stapsdt              0x00000044	NT_STAPSDT (SystemTap probe descriptors)
    Provider: sync
    Name: unlock
    Location: 0x00000000005dd26e, Base: 0x000000000086f360, Semaphore: 0x0000000000000000
    Arguments: 8@(%rdi) 8@24(%rdi) -4@32(%rdi)

I think, this is caused the templates were using - and that's not something we want to change. E.g.

bitcoin/src/sync.cpp

Lines 329 to 333 in f7917c2

template <typename PARENT>
AnnotatedMixin<PARENT>::AnnotatedMixin()
{
TRACE1(sync, mutex_ctor, this);
}

Two ELF notes with the same context/provider and name cause problems for me. E.g., when using bcc via the binary path (as we do it in e.g. the interface tests). According to iovisor/bcc#1774 (comment) this works when using the PID, which we can do for the interface tests. Still, supporting hooking into the tracepoints both via PID and binary path would be good, but I'm not sure if that's possible in this case.

@martinus
Copy link
Contributor Author

there are two ELF notes generated for each sync tracepoint

Currently the templates are instantiated only for std::mutex and std::recursive_mutex, so workaround might be to do something like this:

template <typename PARENT>
AnnotatedMixin<PARENT>::AnnotatedMixin()
{
    if constexpr (std::is_same_v<std::mutex, PARENT>) {
        TRACE1(sync, mutex_ctor, this);
    } else if constexpr (std::is_same_v<std::recursive_mutex, PARENT>) {
        TRACE1(sync, recursive_mutex_ctor, this);
    } else {
        assert(false && "unknown lock used");
    }
}

Unfortunately currently I don't have much time to work on this though

@jb55
Copy link
Contributor

jb55 commented May 18, 2022 via email

@DrahtBot
Copy link
Contributor

DrahtBot commented Oct 3, 2022

🐙 This pull request conflicts with the target branch and needs rebase.

Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft".

@martinus martinus marked this pull request as draft November 30, 2022 18:06
@DrahtBot
Copy link
Contributor

There hasn't been much activity lately and the patch still needs rebase. What is the status here?

  • Is it still relevant? ➡️ Please solve the conflicts to make it ready for review and to ensure the CI passes.
  • Is it no longer relevant? ➡️ Please close.
  • Did the author lose interest or time to work on this? ➡️ Please close it and mark it 'Up for grabs' with the label, so that it can be picked up in the future.

@martinus
Copy link
Contributor Author

Hi, I lost interest / don't have time for this unfortunately. You can close it and mark it as up-for-grabs.

@fanquake fanquake closed this Feb 28, 2023
0xB10C added a commit to 0xB10C/bitcoind-observer that referenced this pull request Mar 14, 2023
@bitcoin bitcoin locked and limited conversation to collaborators Feb 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants