Selecting a tool for debugging

Updated: April 19, 2023

The IDE works with several third-party tools and contains its own features that help you debug improper program results, process hanging, or process crashing.

These debugging use cases are defined as follows:
Improper program results
No fatal error occurs but the application doesn't do what's expected.
Process hanging
The application becomes unresponsive but continues running.
Process crashing
The application exits abruptly, without properly terminating its operations.

Guidelines on selecting a tool for each use case are given below. The debugging capabilities of these tools are listed in Integrated tools.

Improper program results

There are many IDE tools that can help you find logic errors in program code or memory corruption issues. Generally, you should first launch the application with the debugger attached and try to reproduce the bad behavior. If you can't reproduce it, you can wait until you next see the improper results, then attach the debugger to determine what state the program is in and how it got there. For multiprocess programs, you can debug a child process.

Note: If your program runs too slowly but still does what's expected, you shouldn't run the debugger and look for defects. Instead, you should analyze the program's memory and resource usage and analyze its performance, to determine what changes are needed to make it perform better.
If you step through the program's code and examine its memory and variable contents but can't figure out why the application is misbehaving, you can investigate the issue using other tools:
  1. If you think that data is being corrupted, run a memory-checking tool—Memory Analysis, Valgrind Helgrind, or Valgrind Memcheck. These tools identify runtime errors that are often related to memory corruption, such as reads of uninitialized or invalid memory.
  2. If you suspect there's a bad value coming from another process or there's an odd interaction between processes, use the System Profiler to see the system-level activity (e.g., kernel calls, interprocess messaging) on the target. To do so, you must first run a kernel event trace to capture this activity. The System Profiler then lets you view the trace data.
  3. If both these options fail to identify the cause, you must continue using the debugger. You can set more breakpoints to see which code paths are followed when the bad behavior happens.

Process hanging

When you observe an unresponsive application, the IDE can help you inspect the application's current state. This way, you can learn which processes are hanging without having to rerun the application and attempt to reproduce the problem.

The quickest way to start investigating a hanging process is to view the current target machine state through the QNX System Information perspective. Here, the System Resources view lists CPU usage by process, so you can see right away which processes are consuming excessive resources or no resources. You can see thread-level details in the Process Information view, including thread states.

If the System Information doesn't give you a good idea of why a process is hanging, you can use other tools to figure out what the application is doing:
  1. You can run a kernel event trace to capture the system-level activity on the target and then view the trace data with the System Profiler. These data can tell you if a process is spinning (i.e., actively executing but not making progress) and if another process is involved. An example would be livelock, when the interaction between processes is causing them to cycle endlessly between the same states. The trace data can also reveal deadlock. For instance, if a process is waiting on both a mutex and a semaphore, it's likely a case of priority inversion.
  2. To see exactly where the application is stuck, you can attach the debugger to the hanging process. As soon as it attaches to a process, the GDB tool stops execution and shows the current line of code (assuming there are matching binaries on the host). This is particularly helpful for deadlock within a process. Suppose one thread is at the start of a critical section. This likely means it can't acquire a necessary resource and is the cause of the deadlock.
  3. Sometimes, attaching the debugger to find the current execution position and stepping through the code doesn't tell you why the program is stuck. In this case, you can use the Application Profiler to enable sampling-based profiling. This profiling method makes the application report its current line at regular intervals, which tells you if a function is consuming a lot of execution time.
    Note: The Application Profiler won't help with deadlock because the application must be executing code for the tool to receive samples.

Process crashing

When a process crashes on a machine running dumper, a core file is generated and the IDE gives you the option of debugging that core file. If you choose to do so, the IDE downloads it from the target to the host's workspace and launches the debugger. The core file contains the final state of the program, allowing you to see what happened.

If you can reproduce the crash, it's better to launch the application with the debugger attached and do so. The postmortem debugging workflow involving a core file is intended for crashes that are hard to reproduce or for when you don't have access to the device on which the crash occurs.

When the program crashes, GDB stops execution and displays the stack trace, the current line of code, and the last signal received by the program. There are a few options to continue investigating:
  1. If you suspect that memory corruption is behind the crash, run a memory-checking tool—Memory Analysis, Valgrind Helgrind, or Valgrind Memcheck. These tools pinpoint problematic lines of code such as illegal memory accesses or double-freeing attempts.
  2. If you suspect that a bad value coming from another process or even from a thread within the same process is causing the crash, run a kernel event trace. The System Profiler then displays data that lets you see any odd interactions between threads or processes.
  3. If both these options fail to identify the cause, you must continue using the debugger. You can set more breakpoints to see which code paths are followed before the crash occurs.