For combining features, a library interface with explicit API calls generally works better than having separate top-level clients. Separating out concerns, isolating functionality, and creating modular units makes these libraries more re-usable. E.g., where possible it seems better to have a library function that takes in a scratch register, rather than a function that goes and obtains its own scratch registers, pushing all responsibility for which scratches to use up to the top level client, avoiding conflicts and confusion over the scratch strategy. Similarly, for logging or how to handle errors it is best to push that up to a single top-level client. Thus, instead of just having a drcov code coverage client that is run at the top level alongside others, we have a drcovlib code coverage library that can be added to any client (and is used by drmemory for fuzzing features). Instead of just a drsyscall or drstrace client, we have a drsyscall extension library.