If you're used to ASAN, think of Valgrind like ASAN, TSAN, MSAN, and UBSAN all at once at the same time, and then has other facilities like profiling.
In any situation where I have control of how the code is built, I personally prefer to run those various SANs instead of Valgrind, usually three parallel builds: one with -fsanitize=address,integer,undefined, one -fsanitize=thread if more than one thread is involved in the project, and one -fsanitize=memory too if Linux is available. That gets you almost all the benefits of Valgrind faster than Valgrind.
But Valgrind is still an exceptionally powerful tool to learn and use. Its chief advantage to me is to be able to work with an unmodified binary. Its chief downside is it can be slow, and occasionally you may run into issues with support for more obscure instructions like vcvtps2ph.
MSAN is really the star of the sanitizer world, and is worth spinning up a Linux VM for. It tracks the initialized-ness of all your data and catches any use (really, branch) on anything uninitialized. I like to think of ASAN as "did I get my pointers right?" and MSAN as "did I get everything else right?".
If you're not familiar with integer sanitizer, it's like UBSAN except it checks for things that are often mistakes rather than things that are actually undefined. The most common situation I've found is code that's hashing data will often pop up a warning about unsigned integer overflow, which I choose to suppress as finely grained as possible with something like __attribute__((no_sanitize("unsigned-integer-overflow", "unsigned-shift-base"))). That way you can still find unintentional unsigned integer problems (e.g. with size_t).
In any situation where I have control of how the code is built, I personally prefer to run those various SANs instead of Valgrind, usually three parallel builds: one with -fsanitize=address,integer,undefined, one -fsanitize=thread if more than one thread is involved in the project, and one -fsanitize=memory too if Linux is available. That gets you almost all the benefits of Valgrind faster than Valgrind.
But Valgrind is still an exceptionally powerful tool to learn and use. Its chief advantage to me is to be able to work with an unmodified binary. Its chief downside is it can be slow, and occasionally you may run into issues with support for more obscure instructions like vcvtps2ph.
MSAN is really the star of the sanitizer world, and is worth spinning up a Linux VM for. It tracks the initialized-ness of all your data and catches any use (really, branch) on anything uninitialized. I like to think of ASAN as "did I get my pointers right?" and MSAN as "did I get everything else right?".
If you're not familiar with integer sanitizer, it's like UBSAN except it checks for things that are often mistakes rather than things that are actually undefined. The most common situation I've found is code that's hashing data will often pop up a warning about unsigned integer overflow, which I choose to suppress as finely grained as possible with something like __attribute__((no_sanitize("unsigned-integer-overflow", "unsigned-shift-base"))). That way you can still find unintentional unsigned integer problems (e.g. with size_t).