The simple fact of the matter is that reading code is hard, maybe even impossible in the general case. You
can understand code with some amount of effort, but it often boils down to an exercise in reverse engineering.
One thing this means is that in any substantial codebase you are never going to understand all of it. You will typically only have time to learn a fraction of the system, so if you are going to proactively explore the codebase, you will need to prioritize. You probably (but not necessarily) want to get a handle on the top-level architecture before digging deep anywhere.
My final piece of advice is that I personally find it impossible to understand just about any non-trivial piece of code without running it, and running it multiple times (1). Perhaps even many, many times. You can run under a debugger (single stepping or breakpoints) and this seems to work for many people. I still rely on print statements sprinkled through the code myself, adding and removing them as I run the code in question over and over again as my current point of interest moves from place to place in the code. This might sound scary, but it's not that different from the way you normally debug code.
(1) It's entirely possible that the person that wrote the code in the first place also ran it many, many times (testing each small change) as they wrote it. So it's perhaps not unreasonable that you yourself may need to run it many, many times in order to understand it later.