Skip to main content
This guide covers practical debugging techniques for QIM SDK AI pipelines — from enabling verbose logs to measuring per-stage latency, tracing buffers, and diagnosing camera or performance issues.

1. Enable Debug Logs

QIM SDK uses the standard GStreamer debug system. Pass --gst-debug=<level> to gst-launch-1.0, or set the GST_DEBUG environment variable for Python/C++ apps.

Debug Levels

LevelNameDescription
0noneNo debug information is output.
1ERRORLogs all fatal errors. These are errors that do not allow the core or elements to perform the requested action. The application can still recover if programmed to handle the conditions that triggered the error.
2WARNINGLogs all warnings. Typically these are non-fatal, but user-visible problems are expected to happen.
3FIXMELogs all “fixme” messages. Those typically that a codepath that is known to be incomplete has been triggered. It may work in most cases, but may cause problems in specific instances.
4INFOLogs all informational messages. These are typically used for events in the system that only happen once, or are important and rare enough to be logged at this level.
5DEBUGLogs all debug messages. These are general debug messages for events that happen only a limited number of times during an object’s lifetime; these include setup, teardown, change of parameters, etc.
6LOGLogs all log messages. These are messages for events that happen repeatedly during an object’s lifetime; these include streaming and steady-state conditions. This is used for log messages that happen on every buffer in an element for example.
7TRACELogs all trace messages. Those are message that happen very very often. This is for example is each time the reference count of a GstMiniObject, such as a GstBuffer or GstEvent, is modified.
9MEMDUMPLogs all memory dump messages. This is the heaviest logging and may include dumping the content of blocks of memory.

Global Level

gst-launch-1.0 --gst-debug=3 \
filesrc location=... ! ...

Per-Element Level

Override the global level for a specific plugin — useful for isolating one stage without drowning in output from everything else:
# Global = 2 (warnings), but inference plugin = 6 (verbose)
gst-launch-1.0 --gst-debug=2,qtimltflite:6 \
filesrc location=... ! ...

Common Starting Points

FlagWhen to use
--gst-debug=2Production runs — quiet, errors/warnings only
--gst-debug=2,qtimltflite:5Prints all ERROR and WARNING messages globally, plus ERROR, WARNING, INFO, and DEBUG messages specifically from the qtimltflite plugin
--gst-debug=2,qtimlvtransform:4,qtivcomposer:7Prints all ERROR and WARNING messages globally, ERROR, WARNING, and INFO messages from qtimlvtransform, and ERROR, WARNING, INFO, DEBUG, LOG, and TRACE messages from qtivcomposer

Python / C++ Apps

GST_DEBUG=3 python3 my_pipeline.py
GST_DEBUG=5 ./my_pipeline_app

Redirect to File

GST_DEBUG=5 GST_DEBUG_FILE=/data/gst.log GST_DEBUG_COLOR_MODE=off gst-launch-1.0 ...

2. Measure Pipeline KPIs (Latency per Stage)

Use the built-in GStreamer latency tracer to get per-element mean/min/max processing time for every stage — no custom instrumentation required.

Enable Tracing

GST_TRACERS="latency(flags=pipeline+element)" \
GST_DEBUG="GST_TRACER:7" \
gst-launch-1.0 -e \
  qtimlvconverter name=stage_01_preproc \
  qtimltflite name=stage_01_inference \
    model=$HOME/models/$MODEL_NAME \
    delegate=external external-delegate-path=libQnnTFLiteDelegate.so \
    external-delegate-options="QNNExternalDelegate,backend_type=htp,log_level=(string)1;" \
  qtimlpostprocess name=stage_01_postproc module=yolov8 \
    labels=$HOME/labels/$LABELS_NAME \
    settings="{\"confidence\": 51.0}" \
  filesrc location=$HOME/media/$SRC_VIDEO_NAME ! qtdemux ! h264parse ! \
  v4l2h264dec capture-io-mode=4 output-io-mode=4 ! video/x-raw,format=NV12 ! queue ! \
  tee name=t ! qtimetamux name=obj_mux ! qtivoverlay ! \
    fpsdisplaysink name=display sync=false text-overlay=false \
      video-sink="waylandsink" \
  t. ! queue ! stage_01_preproc. stage_01_preproc. ! queue ! \
  stage_01_inference. stage_01_inference. ! queue ! \
  stage_01_postproc. stage_01_postproc. ! text/x-raw ! queue ! obj_mux. \
2> /tmp/trace.log

Parse Results

gst-stats-1.0 /tmp/trace.log

Reading the Output

Element Latency Statistics:
  stage_01_preproc.src:   mean=2.19ms  min=1.24ms  max=12.2ms   ← preprocessing
  stage_01_inference.src: mean=5.71ms  min=4.95ms  max=6.78ms   ← inference (NPU)
  stage_01_postproc.src:  mean=0.24ms  min=0.12ms  max=0.44ms   ← postprocessing
  obj_mux.src:            mean=0.76ms  min=0.14ms  max=2.03ms   ← metadata sync
At 30 fps (33 ms budget per frame), the typical AI pipeline uses ~37% of the frame budget, leaving headroom for additional stages.

Measure FPS Live (On-Screen)

Add fpsdisplaysink in place of waylandsink to see FPS on the display in real time:
... ! qtivoverlay ! fpsdisplaysink sync=false video-sink="waylandsink sync=false"

3. Measure Inference Performance

GST_TRACER (inferences/sec)

From the tracer output, calculate inferences/sec:
inferences/sec = 1 / mean_inference_latency
Example: mean = 5.71 ms1 / 0.00571 ≈ 175 inferences/sec

pidstat (CPU/Memory alongside inference)

# Run in a second terminal while the pipeline is running
pidstat -u -r -p $(pgrep gst-launch-1.0) 1
This reports per-second CPU (%usr, %sys) and memory (RSS, VSZ) for the pipeline process.

4. Debug Performance Issues

Frame Drops

Frame drops manifest as:
  • Stuttering on the display
  • queue elements reporting overflows in debug logs (--gst-debug=4)
  • Tracer showing high max latency on inference (stage_01_inference)
Check for queue overflows:
gst-launch-1.0 --gst-debug=4,queue:5 ...
Look for lines like:
WARN   queue  gstqueue.c:... overrun ...
Fix: Add max-size-time=0 max-size-bytes=0 max-size-buffers=3 to queues around inference:
... ! queue max-size-buffers=3 ! stage_01_preproc. ...

Slow Throughput

If end-to-end latency is higher than expected:
  1. Run the latency tracer to find which stage is the bottleneck.
  2. Check if the inference delegate is correct — GPU delegate is much slower than HTP (NPU):
# Slow — GPU
qtimltflite delegate=gpu ...

# Fast — NPU via HTP
qtimltflite delegate=external \
  external-delegate-path=libQnnTFLiteDelegate.so \
  external-delegate-options="QNNExternalDelegate,backend_type=htp;"
  1. Check model quantization — W8A8 (quantized) is significantly faster on the NPU than float32.

Memory Copies

Unnecessary buffer copies add latency. To detect them, enable memory tracing:
GST_DEBUG=4,GST_BUFFER:5 gst-launch-1.0 ... 2>&1 | grep -i "copy\|memcpy\|alloc"
Use capture-io-mode=4 output-io-mode=4 on decoder/encoder elements to enable zero-copy DMA buffer passing:
v4l2h264dec capture-io-mode=4 output-io-mode=4

5. Trace Buffers

Buffer tracing lets you follow a specific buffer through the pipeline to diagnose where it is dropped, delayed, or corrupted.

Enable Buffer Tracing

GST_DEBUG=GST_SCHEDULING:5 gst-launch-1.0 ... 2>&1 | head -200
Each line shows a buffer’s PTS (presentation timestamp), DTS, size, and which pad it passed through.

Check for PTS Discontinuities

If frames are out of order or the pipeline stalls, look for timestamp jumps:
GST_DEBUG=GST_SCHEDULING:5 gst-launch-1.0 ... 2>&1 | grep -i "pts\|discont\|gap"

Use the Dot Graph (Pipeline Visualization)

Dump the pipeline as a .dot file to visually inspect element connections:
GST_DEBUG_DUMP_DOT_DIR=/tmp gst-launch-1.0 -e ... &
# After pipeline is running:
dot -Tpng /tmp/*.dot -o pipeline.png

6. Debug Camera Issues

Common Errors

QMMF Recorder StartCamera Failed:
ERROR qticamsrc qmmf_source_context.cc: QMMF Recorder StartCamera Failed!
WARN  qticamsrc qmmf_source.c: error: Failed to Open Camera!
Fix: Kill and restart the camera server:
sudo killall cam-server
Then re-run the pipeline. The camera server restarts automatically on the next gst-launch-1.0 invocation.

Enable Camera Server Debug Logs

GST_DEBUG=2,qticamsrc:6 gst-launch-1.0 \
  qticamsrc name=camsrc ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
  waylandsink fullscreen=true sync=false

Camera Not Detected

# List available V4L2 devices
v4l2-ctl --list-devices

# Check camera capabilities
v4l2-ctl -d /dev/video0 --all

ISP Camera (Config #2 / qticamsrc)

To switch from libcamera (Config #1) to qticamsrc (Config #2), write "camx" to the EFI variable:
echo -n "camx" > /var/data
efivar -n 882f8c2b-9646-435f-8de5-f208ff80c1bd-VendorDtbOverlays -w -f /var/data
sync && reboot

Verify Camera Stream is Running

# Quick sanity check — should show frames on display
gst-launch-1.0 \
  qticamsrc name=camsrc ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! \
  waylandsink fullscreen=true sync=false

7. Quick Diagnostic Checklist

Run this sequence when a pipeline doesn’t work:
# Step 1 — Check GStreamer can see the plugins
gst-inspect-1.0 qtimltflite
gst-inspect-1.0 qtimlvconverter
gst-inspect-1.0 qticamsrc

# Step 2 — Run with level 3 logs
gst-launch-1.0 --gst-debug=3 [your pipeline] 2>&1 | grep -E "ERROR|WARN|FIXME"

# Step 3 — Check camera server
sudo killall cam-server   # reset if camera errors appear

# Step 4 — Add latency tracing
GST_TRACERS="latency(flags=pipeline+element)" GST_DEBUG="GST_TRACER:7" \
  gst-launch-1.0 [your pipeline] 2> /tmp/trace.log
gst-stats-1.0 /tmp/trace.log

8. Report an Issue

When filing a bug or asking for help, include:
  1. Platform — Device model (e.g. RB3 Gen 2), OS (Ubuntu / Qualcomm Linux), QIM SDK version
  2. Full pipeline command — the exact gst-launch-1.0 or Python script
  3. Debug log — captured with --gst-debug=5 2> debug.log
  4. Tracer output — from gst-stats-1.0 if a performance issue
  5. Error message — the exact error line from the log
  6. Expected vs actual behaviour
# Collect everything in one command
GST_DEBUG=5 GST_TRACERS="latency(flags=pipeline+element)" GST_DEBUG_FILE=/tmp/debug.log \
  gst-launch-1.0 [your pipeline]
gst-stats-1.0 /tmp/debug.log >> /tmp/debug.log
# Attach /tmp/debug.log to your issue