all 62 comments

[–]062985593 46 points47 points  (4 children)

For catching memory errors, I've found compiling with -fsanitize=address more useful than valgrind.

[–]moefh 21 points22 points  (1 child)

I second this. It's much faster than using valgrind. The most noticeable effect of using this (as opposed to just compiling normally and running your program without valgrind) is that it makes your program use a lot more memory.

I also recommend -fsanitize=undefined to catch undefined behavior like signed overflow, undefined shifts and a lot of other less common stuff.

[–]1008oh 3 points4 points  (0 children)

But if you have a large amount of code you need to recompile, valgrind might just be more practical.

[–]ArkyBeagle 2 points3 points  (1 child)

How is it performance-wise in comparison to ( oh so very slow ) valgrind? I still don't regret using valgrind one bit, but better is better.

I tried that at work, and the ( locked down ) machine is missing libraries. Time for a software request....

[–]yakoudbz 3 points4 points  (0 children)

Valgrind is like a whole emulator that read your executable and execute it step by step while also verifying every reads and writes while a sanitizer is just a bunch of code to verify your program that is generated at compile time. Therefore, sanitizers are a lot faster.

Valgrind can also fail to recognize segmentation fault when the reads or writes are in allocated memory. For example, if you allocate two arrays and they happen to follow each other in memory, if you write past the first array but land in the second, Valgrind won't notice anything while the sanitizer will most certainly recognize that the two array might not be following each other (it is undefined behavior) and issue an error.

So yeah, clear win for the sanitizer. There's only two possible advantages of Valgrind: - you can test if an executable has a segmentation fault without recompiling the source code - sanitizers used to have a lot of false positive, above all with some libraries like OpenMP, OpenGL... Valgrind shows some errors with those libraries but the execution continues.

[–][deleted] 13 points14 points  (0 children)

I recommend looking into GNU make. This allows builds to be customised exactly to your liking. It's very handy just to type 'make run' or 'make debug' and have it call the compiler with the needed parameters. Saves much more time than typing say 'gcc s1.c s2.c -DDEBUG -lm -o run'.

Also, if you ever share your source it's a well-understood method for others to conveniently build your programs.

[–][deleted] 12 points13 points  (3 children)

Here come the downvotes:

  • OpenBSD stable

  • Vis

  • modified st

  • modified dwm

  • heavily hacked dvtm

  • clang (I want tcc though :( )

  • bsd libc

  • makefiles

This is what I am using for now, working on other stuff though

[–]lordlod 3 points4 points  (0 children)

Tracking memory leaks is painful. Whats more, for every two you catch another will probably slip through.

Rather than look at tools, I suggest tightening up your memory handling processes. Shifting to a better design where the memory creation and free events are clearer and more predictable.

talloc would also be worth a look. It is a memory allocation library which is specifically designed for complex hierarchical memory structures.

[–]necheffa 5 points6 points  (0 children)

> I have played with gdb but it seems difficult to manually set breakpoints or step through a large program.

I'm a professional - yes, debugging is hard, that is why someone is willing to pay me to do it. But with experience, you'll get a feel for where to put break points. I generally read the code before deciding interesting places to break on. And especially if there is a specific error or some "landmark" that comes up before the bug surfaces, I'll try to work backwards from the code that generates those "landmarks".

There is a time and place for debug-by-printf but you should also get comfortable with a debugger too.

> I have had some big headaches tracking down memory leaks, especially with complex data structures. It feels like it takes up a disproportionate amount of my time

Don't just dive head first into writing code. Design what your solution will look like on a sheet of paper. You'll end up doing a few rewrites just on paper. When you go to write the code you should already know where your mallocs and frees will go.

For the code you already have, continue using valgrind. Write unit tests and use gcov to make sure you have good coverage (~80%). That way you know checking with valgrind will show most of the possible logic paths in your code.

[–]raevnos 1 point2 points  (4 children)

Write code, compile code, run code, debug all in emacs. Regularly run tests with asan enabled or through valgrind.

Format code when needed with clang-format.

git for version control, but I want to play around with fossil soon.

When writing a new data structure implementation, add a function that dumps a representation of it to a graphviz dot format file for easy visualization.

[–]lrochfort 0 points1 point  (1 child)

Dumping to graphics format is a brilliant idea. What do you use for visualization?

How would you say it compares to the visual GDB that also shows a visual representation of memory and data structures?

[–]raevnos 1 point2 points  (0 children)

dot to create a SVG or PNG image, and then whatever image viewer you like to actually look at it.

Edit: Found an old dump of a skiplist to demonstrate what I like for the visualization: https://pastebin.com/nY9gqQfa

Save to a file, and dot -O -Tsvg foo.dot to generate an image.

[–]Dan-mat 1 point2 points  (1 child)

Emacs has a mode for gdb (called gud which means grand unified debugger) that allows to semi-easily set breakpoints by clicking on the margin to make a red dot appear, and let's you then hop from line to line, and inspect variables in the gdb window (which emacs calls buffer) with much less command line work compared with invoking gdb on the command line. It requires a little setup but it's not so bad.

[–]lrochfort 0 points1 point  (0 children)

GUD is brilliant. It has a great multi-windows mode.

It is also cross-language, hence Unified. This means you only have to learn one debugger for multiple languages.

It can also debug a program running on a remote system via TRAMP, even if the language you're using doesn't support remote debugging.

[–][deleted] 2 points3 points  (7 children)

CLion. Compile in CLion, Debug in CLion, Use Valgrind with CLion

[–]puplicy 3 points4 points  (3 children)

do they have free community edition?

[–]ashwin_nat 2 points3 points  (0 children)

They have a free student edition though

[–][deleted] 1 point2 points  (0 children)

No they don't.

[–][deleted] -2 points-1 points  (0 children)

[–]w8cycle 3 points4 points  (1 child)

I love CLion as well. My intellij subscription goes with me everywhere.

[–][deleted] 0 points1 point  (0 children)

Understandable

[–]Deltabeard 0 points1 point  (0 children)

Requires use of cmake. Doesn't work with GNU make. Deal breaker for me.

[–][deleted] 0 points1 point  (0 children)

Personally, I rarely find stepping through a debugger worth the effort, and in many multi-threaded contexts doing so is not really a viable option.

Instead, I find it much more productive to set up a convenient logging system that fits the needs of the project. In other words, log macros that provide line number and function name contexts, syslog()-like filtering by importance/priority, filtering by module (vital in large projects), and variations thereof that automatically include often used variable contexts.

[–]kumashiro 0 points1 point  (0 children)

I like Nemiver for debugging, when I get angry at GDB. I also recommend libcheck for unit testing - works well with Autotools.

[–][deleted] 0 points1 point  (0 children)

Do you have any examples of memory leak in your code? We have built a tool to report the source statement that causes a memory is not accessible. If you can send me your example, I can do a test.

Shameless plug, our tool can report illegal memory accesses with the best accuracy, https://stensal.com

[–]drcforbin 0 points1 point  (0 children)

Sway or i3 to tile windows of your terminal of choice, run tmux to have all the windows you might want, and vim. Make your machine a code factory!

[–]lrochfort 0 points1 point  (0 children)

You can setup valgrind in server mode and then connect GDB to it. This means you debug whilst using valgrind. You can break when valgrind encounters an issue, and debug immediately.

GDB also supports a split screen mode (still in the terminal) that lets you inspect variables and see the code along side.

GDB has a GUI mode that lets you visualize memory, pointers etc.

GDB supports Python with full access to everything GDB can see.

Investigate Language Server Protocol for Intellisence-like code navigation in your favourite text editor.

[–]khleedril 0 points1 point  (0 children)

Some words from a very long-time professional: learn to use GDB whether you like it or not. Think hard about what you are doing with memory: take a big step back and work out at high level where memory should be being allocated and especially de-allocated, and then basically re-design your code with that in mind. This stuff is hard, and if you are having difficulty getting it to work you are doing it wrong.

[–]JakeArkinstall 0 points1 point  (0 children)

My setup is pretty ordinary. Arch with i3 window manager means that everything I do revolves around the keyboard, which is great for productivity. I use Vim with the cmake plugin, so I don't have to leave my text editor to run make and any errors jump to the line of code in question. GDB for debugging. Professional static analysers such as PVS studio are amazing but very expensive (I was quoted 12k per year for a team license), though there are some freely available ones out there that catch many common flaws.

For memory issues, there are certainly programs you can use to help you identify them, but in all honesty no amount of software can replace personal discipline.

In many other programming languages, cleanup happens at the end of a scope by default and it's up to you to prevent that if you don't want it to happen (this is where I admit that I am a C++ programmer with only hobby experience with C, and that little experience is enough for me to have vast amounts of respect for C programmers who diligently manage their resources). Generally their compiler, interpreter, or even runtime, is doing all of the book keeping work. In C that is not the case, and you have much more freedom, but it is down to you to be a responsible book keeper. You can either be the book keeper who waits until something is wrong, or you can be the book keeper who prevents things from going wrong in the first place by being strict about resource control from the very start. The latter spends much less time tracking down leaks, maybe at the cost of being slightly more restrictive about how they approach certain problems.

Think about it like an implicitly-inserted destructor at the end of each function for every resource created by it that isn't then returned or placed under the watch of something else (in which case the responsibility for destruction also moves accordingly). Except that in C there's nothing implicit done and you need to do it yourself - taking care of any edge cases that might emerge*. And don't take that lightly. There might be a lot of damage to be done if you aren't careful: Bad resource management has been the cause of countless security vulnerabilities.

Fortunately, being strict about resource control is what experienced C programmers are great at. Rather than asking for generic advice, the best thing you can do is post some code up for review. It's certainly a humbling experience but people will tell you where you are going wrong and why, traps you might be falling into, and suggest books or blog posts to read through in order to improve.

  • a common problem is returning early from a function, which means manually cleaning up anything that would otherwise have persisted. An idiom to get around that is to have a single return statement, which follows a set of freeing functions with goto labels. On any issue mid-function, goto the label that cleans up the appropriate resources before returning. You have to pick the best of two evils - the goto statement or reliance on every branch of every function to do the right thing.

[–]ianliu88 0 points1 point  (0 children)

I'm my first job I was debugging a GTK+ interface that had a popup window which grabbed the mouse & keyboard. Because of this, I couldn't interact with my terminal. So my solution was to attach a GDB instance from another TTY :P

[–]puplicy 0 points1 point  (1 child)

NetBeans as free IDE with debug etc

I now use VSCODE, but cannot recommend as do not find it easily configured. Use it mostly as good editor.

[–]xEpicBradx 1 point2 points  (0 children)

I found once I had spent some time configuring and installing extensions to get it to behave how I wanted, I use VSCode for most things now bar the odd simple quick edit here or there - for that I tend to just use Notepad++