I use Valgrind's helgrind.
In fact, Valgrind is generally my favorite debugging tool: memcheck (default) for memory errors and leaks, callgrind for profiling, massif for memory profiling and finding "stealth" leaks, helgrind for multi-threading and a few others that I use less often.
The great thing about Valgrind is that it doesn't require any instrumentation at build time (though debug symbols are recommended), just run "valgrind your_program". Behind the scenes, it is actually a VM, that's how it can work with binaries directly. In theory it works with any language, and no need to do anything funny with libraries, kernel stuff, etc...
The biggest problem is that the performance penalty is huge, 10x is typical but 100x is not uncommon. ThreadSanitizer is slow but not that slow, I don't know which one is the best at finding issues, I think they are on par, but when you are an a particularly hairy problem, it is good to have both.