Tutorial Feedback

Use the form below to provide feedback on this page / tutorial as a whole.

Skip to content

Setting up Coverage Data#

Steps 1–6 walk through setting workspace filters, choosing a file, scattering breakpoints across it, generating traffic, and validating the snapshot data before building the dashboard.


Step 1: Set Workspace Filters#

dtctl resolves the Live Debugger workspace from the current working directory. Before creating breakpoints, tell it which workload to target:

dtctl update breakpoint --filters k8s.namespace.name:easytrade

Replace easytrade with your actual namespace if different. You can also filter by a specific process group instance:

dtctl update breakpoint --filters k8s.namespace.name:easytrade,dt.entity.process_group_instance:PROCESS_GROUP_INSTANCE-1234567890ABCDEF

Step 2: Pick a File and Identify Candidate Lines#

For this example, use OrderController.java from the EasyTrade creditcardorderservice. The same pattern works for any file type that Live Debugger can instrument.

SRC=easytrade/src/credit-card-order-service/src/main/java/com/dynatrace/easytrade/creditcardorderservice/OrderController.java
FILE=$(basename "$SRC")

Start by listing the file with line numbers to get a feel for its structure:

nl -ba "$SRC" | sed -n '100,340p'

At this point you have two choices:

  • Manual placement — choose exact lines that matter
  • Scatter placement — generate a broader set of candidate lines and instrument them in bulk

For coverage-style analysis, scatter placement is the better fit.


Step 3: Scatter Breakpoints Across Executable-Looking Lines#

A simple way to generate candidate lines is to skip blank lines and obvious comment-only lines, then sample the rest.

SRC=easytrade/src/credit-card-order-service/src/main/java/com/dynatrace/easytrade/creditcardorderservice/OrderController.java
FILE=$(basename "$SRC")

awk '
  {
    line = $0
    sub(/^[[:space:]]+/, "", line)
    sub(/[[:space:]]+$/, "", line)
  }
  line == "" { next }
  line ~ /^\/\// { next }
  line ~ /^\/\*/ { next }
  line ~ /^\*/ { next }
  line ~ /^@/ { next }
  line ~ /^(package|import)[[:space:]]/ { next }
  line ~ /^[{}]+$/ { next }
  line ~ /^(public|protected|private|abstract|final|sealed|non-sealed)?[[:space:]]*(class|interface|enum|record)[[:space:]]/ { next }
  line ~ /^(public|protected|private|static|final|abstract|synchronized|native|default|strictfp)[[:space:]]/ && line ~ /\)[[:space:]]*(throws[[:space:]].*)?\{?$/ { next }
  { print NR }
' "$SRC" \
  | shuf \
  | head -n 20 \
  | sort -n \
  > breakpoint-lines.txt

cat breakpoint-lines.txt

That gives you a reproducible list of candidate line numbers while skipping obvious non-executable lines such as comments, annotations, imports, braces, class declarations, and likely method signatures.

Create one Live Debugger breakpoint per line:

while read -r line; do
  echo "Creating breakpoint at ${FILE}:${line}"
  dtctl create breakpoint "${FILE}:${line}"
done < breakpoint-lines.txt

If you want to start with a smaller hand-picked set before scattering wider, use a file like this instead:

cat > breakpoint-lines.txt <<'EOF'
113
115
121
132
157
188
214
241
267
300
306
EOF

Then run the same loop.


Step 4: Verify the Breakpoint Set#

List the breakpoints in the current workspace:

dtctl get breakpoints

Inspect a specific location:

dtctl describe breakpoint "OrderController.java:306"
Note that the breakpoint status should be 'Active' and not 'Pending' to capture breakpoint data.

Breakpoints listed with enabled status

If you want a structured inventory of the scatter set:

dtctl get breakpoints -o json

Store the total number of instrumented lines as your denominator for the dashboard:

TARGET_LINES=$(wc -l < breakpoint-lines.txt | tr -d ' ')
echo "$TARGET_LINES"

If TARGET_LINES=11, your dashboard can compute a percentage from snapshot activity alone.


Step 5: Generate Traffic Through the File#

Now hit the application path that exercises the file. In this case, the EasyTrade application has a load generator running which sends traffic through our file of interest, the OrderController.java.

But this can also be:

  • A curl loop against the service endpoint
  • An integration test suite
  • A replay workload
  • A synthetic monitor
  • A workflow that calls the service

For example:

for _ in $(seq 1 50); do
  curl -sS "https://your-service.example/api/orders/latest-status" > /dev/null
done

Each time one of your scattered breakpoints is hit, Live Debugger emits a snapshot record into application.snapshots.


Step 6: Inspect Raw Coverage Data from application.snapshots#

Before building the dashboard, validate the data path directly with dtctl query.

Most recent snapshots for the file#

dtctl query '
fetch application.snapshots
| filter code.filepath == "OrderController.java"
| sort timestamp desc
| fields timestamp, code.filepath, code.function, code.line.number, snapshot.id, trace.id
| limit 20
'

Distinct hit lines in the file#

dtctl query '
fetch application.snapshots
| filter code.filepath == "OrderController.java"
| summarize covered_lines = countDistinct(code.line.number)
'

Hits by function and line#

dtctl query '
fetch application.snapshots
| filter code.filepath == "OrderController.java"
| fieldsAdd line = toLong(code.line.number)
| summarize hits = count(), by: { code.function, line }
| sort code.function asc, line asc
'

Coverage summary for the scattered set#

Replace 11 with the number of breakpoints you actually created.

dtctl query '
fetch application.snapshots
| filter code.filepath == "OrderController.java"
| summarize total_hits = count(), covered_lines = countDistinct(code.line.number), functions_hit = countDistinct(code.function)
| fieldsAdd target_lines = 11
| fieldsAdd coverage_pct = round(100.0 * covered_lines / target_lines, decimals: 2)
'

This is the core trick: the numerator comes from distinct line numbers in application.snapshots, and the denominator comes from the set of lines you intentionally instrumented.