
CTAL-TTA Static and Dynamic Analysis: Code Quality and Runtime Testing
Static and dynamic analysis are complementary techniques that examine software quality from different perspectives. Static analysis inspects code without executing it, finding structural issues and potential defects early. Dynamic analysis examines the running application, detecting runtime behaviors like memory leaks and performance bottlenecks that only manifest during execution.
Mastering both approaches is essential for the CTAL-TTA certification and for technical test analysts who need to ensure code quality at every level.
Table Of Contents-
- Understanding Static vs Dynamic Analysis
- Static Code Analysis Fundamentals
- Static Analysis Tool Categories
- Code Metrics and Complexity Analysis
- Code Review Techniques
- Dynamic Analysis Fundamentals
- Memory Analysis and Leak Detection
- Performance Profiling
- Integrating Analysis into Development Workflow
- Tool Selection and Best Practices
Understanding Static vs Dynamic Analysis
Key Differences
| Aspect | Static Analysis | Dynamic Analysis |
|---|---|---|
| Execution | Code NOT executed | Code IS executed |
| Timing | Before/during compile | During runtime |
| Defects found | Structural, syntactic, potential bugs | Actual runtime behaviors |
| Coverage | Entire codebase | Only executed paths |
| Speed | Generally faster | Requires test execution |
| False positives | Higher rate | Lower (actual observation) |
When to Use Each
Static Analysis is Best For:
- Finding coding standard violations
- Detecting potential null pointer dereferences
- Identifying security vulnerabilities in code patterns
- Measuring code complexity
- Ensuring consistent code style
- Finding dead/unreachable code
Dynamic Analysis is Best For:
- Detecting memory leaks
- Finding race conditions
- Measuring actual performance
- Identifying resource bottlenecks
- Validating error handling behavior
- Testing concurrent code
Complementary Approaches: Neither technique alone provides complete coverage. Static analysis finds issues that might never occur at runtime; dynamic analysis finds issues that static analysis cannot predict. Professional testing strategies use both.
Static Code Analysis Fundamentals
Types of Static Analysis
1. Control Flow Analysis
Examines the order of statement execution:
def process_data(data):
if data is None:
return None
result = transform(data)
return result
print("Done") # Unreachable code detected!Detects:
- Unreachable code
- Infinite loops
- Missing return statements
- Dead code after unconditional returns/breaks
2. Data Flow Analysis
Tracks how data moves through the program:
def calculate_total(items):
total = 0
for item in items:
subtotal = item.price * item.quantity
# Bug: total never updated!
return total # Always returns 0Detects:
- Uninitialized variables
- Unused variables (defined but never read)
- Variables overwritten without being used
- Potential null/undefined dereferences
3. Compliance Analysis
Checks code against coding standards:
// Style violation: Class name should be PascalCase
public class myClass { // Should be: MyClass
// Violation: Magic number
public static final int TIMEOUT = 30; // OK
int delay = 30; // Violation: use constant
}Standards Checked:
- Naming conventions
- Code formatting
- Documentation requirements
- Language-specific best practices
What Static Analysis Cannot Find
Limitations:
- Runtime-dependent behavior
- Actual performance issues
- Timing-dependent bugs (race conditions)
- Environment-specific problems
- Business logic correctness
def get_user_age(user_id):
user = database.fetch_user(user_id)
return user.age # Static analysis: looks fine
# Runtime: user could be None!Static analysis sees the code structure but cannot predict that fetch_user might return None for invalid IDs.
Static Analysis Tool Categories
Linting Tools
Focus on code style and simple error patterns:
| Language | Tools |
|---|---|
| Python | Pylint, Flake8, Ruff |
| JavaScript | ESLint, JSHint |
| Java | Checkstyle, PMD |
| C/C++ | cpplint, cppcheck |
| Go | golint, staticcheck |
Example ESLint Rule Violations:
// Rule: no-unused-vars
const unusedVariable = 5; // Warning!
// Rule: eqeqeq (require strict equality)
if (value == null) { } // Use === instead
// Rule: no-var
var oldStyle = true; // Use let or constSecurity Analysis Tools (SAST)
Static Application Security Testing finds vulnerabilities:
| Tool | Focus |
|---|---|
| SonarQube | Multi-language, comprehensive |
| Checkmarx | Enterprise security scanning |
| Fortify | Enterprise SAST |
| Snyk Code | Developer-focused |
| Semgrep | Customizable patterns |
Security Issues Detected:
# SQL Injection vulnerability
def get_user(user_input):
query = "SELECT * FROM users WHERE id = " + user_input # DANGER!
# Should use: query = "SELECT * FROM users WHERE id = %s", (user_input,)
# Path Traversal vulnerability
def read_file(filename):
with open("/data/" + filename) as f: # DANGER!
return f.read()
# Attack: filename = "../../../etc/passwd"
# Hardcoded credentials
password = "admin123" # DANGER: Hardcoded secret!Comprehensive Analysis Platforms
SonarQube Categories:
| Category | Description |
|---|---|
| Bugs | Code that is demonstrably wrong |
| Vulnerabilities | Security weaknesses |
| Code Smells | Maintainability issues |
| Security Hotspots | Require manual review |
| Technical Debt | Time to fix issues |
Code Metrics and Complexity Analysis
Cyclomatic Complexity
Definition: Measures the number of independent paths through code.
Calculation Methods:
Method 1: Decision Points
V(G) = Number of decision points + 1Decision points: if, while, for, case, catch, &&, ||, ?:
Method 2: Control Flow Graph
V(G) = E - N + 2P
Where:
E = Edges (flow connections)
N = Nodes (statements)
P = Connected components (usually 1)Practical Example:
def process_order(order): # Node 1
if order.total > 1000: # Decision 1
if order.is_member: # Decision 2
apply_premium_discount()
else:
apply_standard_discount()
elif order.total > 500: # Decision 3 (elif counts)
apply_small_discount()
if order.expedited: # Decision 4
add_expedited_shipping()
return finalize_order() # Node NComplexity: 4 decisions + 1 = 5
Complexity Risk Levels
| Complexity | Risk Level | Testing Implication |
|---|---|---|
| 1-10 | Low | Easy to test and maintain |
| 11-20 | Moderate | More testing effort needed |
| 21-50 | High | Difficult to test, consider refactoring |
| 51+ | Very High | Untestable, refactoring mandatory |
⚠️
Exam Point: Cyclomatic complexity equals the minimum number of test cases required for 100% branch coverage. A function with complexity 15 needs at least 15 tests for full branch coverage.
Other Important Metrics
Lines of Code (LOC) Metrics:
| Metric | Description |
|---|---|
| Physical LOC | Total lines including blanks and comments |
| Logical LOC | Executable statements only |
| SLOC | Source lines of code |
| CLOC | Comment lines of code |
Coupling Metrics:
| Metric | Meaning | Target |
|---|---|---|
| Afferent coupling (Ca) | Classes that depend on this | Low = stable |
| Efferent coupling (Ce) | Dependencies of this class | Low = independent |
| Instability (I) | Ce/(Ca+Ce) | 0 = stable, 1 = unstable |
Cohesion Metrics:
| Metric | Description |
|---|---|
| LCOM | Lack of Cohesion of Methods |
| High LCOM | Class does too many things |
| Low LCOM | Class is focused (good) |
Halstead Complexity Measures
Based on operators and operands:
| Measure | Formula | Meaning |
|---|---|---|
| Vocabulary (n) | n1 + n2 | Unique operators + operands |
| Length (N) | N1 + N2 | Total operators + operands |
| Volume (V) | N × log2(n) | Size of implementation |
| Difficulty (D) | (n1/2) × (N2/n2) | Error proneness |
| Effort (E) | D × V | Implementation effort |
Code Review Techniques
Types of Reviews
| Review Type | Formality | Participants | Documentation |
|---|---|---|---|
| Inspection | High | 3-6 people, defined roles | Formal reports |
| Technical Review | Medium | Peers, moderator | Issue tracking |
| Walkthrough | Medium | Author presents | Meeting notes |
| Pair Programming | Low | Two developers | Code itself |
| Desk Check | Low | Individual | Informal notes |
Technical Review Focus Areas for TTAs
Code Correctness:
# Review: Is the boundary condition correct?
def is_valid_age(age):
return age >= 0 and age <= 120 # Should 120 be included?
# What about age exactly 0?Error Handling:
# Review: Are all failure modes handled?
def fetch_user_data(user_id):
try:
response = api.get_user(user_id)
return response.json()
except RequestException:
return None # Is None appropriate? Log the error?
# What about timeout vs connection error?Resource Management:
# Review: Are resources properly released?
def process_file(path):
f = open(path) # File handle leak if exception occurs
data = f.read()
f.close()
return process(data)
# Better:
def process_file(path):
with open(path) as f: # Guaranteed cleanup
return process(f.read())Review Checklists
Security Review Checklist:
[ ] Input validation present for all external data
[ ] SQL queries use parameterized statements
[ ] No hardcoded credentials or secrets
[ ] Sensitive data encrypted in transit and at rest
[ ] Authentication checks on all protected endpoints
[ ] Authorization verified before sensitive operations
[ ] Error messages don't expose internal details
[ ] Logging doesn't include sensitive dataPerformance Review Checklist:
[ ] N+1 query patterns avoided
[ ] Appropriate caching implemented
[ ] Large collections processed in batches
[ ] Database indexes used for frequent queries
[ ] Expensive operations happen asynchronously
[ ] Connection pooling configured
[ ] Memory allocation optimized for hot pathsDynamic Analysis Fundamentals
What Dynamic Analysis Detects
Runtime-Only Issues:
| Issue Type | Example |
|---|---|
| Memory leaks | Allocated memory never freed |
| Race conditions | Two threads accessing shared data |
| Deadlocks | Threads waiting for each other |
| Buffer overflows | Writing beyond array bounds |
| Use-after-free | Accessing freed memory |
| Resource exhaustion | File handles, connections leaked |
Dynamic Analysis Techniques
1. Instrumentation
Adding monitoring code to track behavior:
# Manual instrumentation example
def original_function(x):
return x * 2
def instrumented_function(x):
start_time = time.time()
call_count[function_name] += 1
result = x * 2
execution_time = time.time() - start_time
log_metrics(function_name, execution_time)
return result2. Sampling
Periodically capturing program state:
- Less overhead than full instrumentation
- Statistical picture of behavior
- Used by many profilers
3. Tracing
Recording detailed execution history:
- Function calls and returns
- System calls
- Memory allocations
Memory Analysis and Leak Detection
Types of Memory Issues
Memory Leak:
void process_requests() {
while (running) {
Request* req = malloc(sizeof(Request));
handle_request(req);
// Missing: free(req); <- Memory leak!
}
}Use-After-Free:
char* create_message() {
char* msg = malloc(100);
strcpy(msg, "Hello");
free(msg);
return msg; // DANGER: returning freed memory!
}Buffer Overflow:
void copy_name(char* input) {
char buffer[10];
strcpy(buffer, input); // DANGER: no bounds checking!
// If input > 10 chars, overflows buffer
}Memory Analysis Tools
| Tool | Platform | Features |
|---|---|---|
| Valgrind | Linux | Memory leaks, invalid access |
| AddressSanitizer | GCC/Clang | Fast memory error detection |
| Dr. Memory | Windows/Linux | Memory debugging |
| Purify | Commercial | Comprehensive analysis |
| Visual Studio | Windows | Debug heap analysis |
Valgrind Example Output:
==12345== HEAP SUMMARY:
==12345== in use at exit: 1,024 bytes in 4 blocks
==12345== total heap usage: 100 allocs, 96 frees
==12345==
==12345== 1,024 bytes in 4 blocks are definitely lost
==12345== at 0x4C2FB0F: malloc
==12345== by 0x401234: process_request (server.c:45)
==12345== by 0x401456: main (server.c:78)Memory Analysis in Managed Languages
Java/JVM memory issues:
// Memory leak via static collection
public class Cache {
private static Map<String, Object> cache = new HashMap<>();
public void addToCache(String key, Object value) {
cache.put(key, value); // Never removed = memory leak!
}
}
// Memory leak via unclosed resources
public void readFile(String path) throws IOException {
FileInputStream fis = new FileInputStream(path);
// Processing...
// Missing fis.close() = resource leak
}Tools for JVM:
- JProfiler
- YourKit
- VisualVM
- Eclipse Memory Analyzer (MAT)
Garbage Collection Misconception: Managed languages with garbage collection still have memory leaks. If objects remain reachable (even unintentionally), they won't be collected. Common causes: static collections, listeners not unregistered, caches without eviction.
Performance Profiling
Types of Profilers
CPU Profilers:
- Identify slow functions
- Find CPU hotspots
- Measure function call counts
Memory Profilers:
- Track allocations
- Find memory hotspots
- Analyze object lifecycle
I/O Profilers:
- Monitor file operations
- Track network calls
- Measure database queries
Profiling Metrics
| Metric | Description | Action if High |
|---|---|---|
| Self Time | Time in function only | Optimize algorithm |
| Total Time | Self + called functions | Check call hierarchy |
| Call Count | How often called | Reduce calls, cache |
| Allocation Rate | Memory per second | Reduce allocations |
Interpreting Profiler Output
Function Self Time Total Time Calls
--------------------------------------------------------
main() 0.1% 100.0% 1
process_orders() 0.5% 85.0% 1
validate_order() 2.0% 30.0% 10000
check_inventory() 25.0% 25.0% 10000 <- HOTSPOT!
calculate_total() 5.0% 50.0% 10000
apply_discount() 40.0% 40.0% 10000 <- HOTSPOT!Analysis:
check_inventory()andapply_discount()are hotspots- Called 10,000 times each
- Together consume 65% of execution time
- Optimize these functions first
Thread Analysis
Thread Dump Analysis:
"Worker-1" BLOCKED on lock owned by "Worker-2"
at com.example.Service.processA()
waiting to lock <0x12345678> (a java.lang.Object)
which is held by "Worker-2"
"Worker-2" BLOCKED on lock owned by "Worker-1"
at com.example.Service.processB()
waiting to lock <0x87654321> (a java.lang.Object)
which is held by "Worker-1"Diagnosis: Deadlock detected - Worker-1 and Worker-2 are waiting for each other.
Integrating Analysis into Development Workflow
CI/CD Integration
Static Analysis in Pipeline:
# Example GitHub Actions workflow
name: Code Quality
on: [push, pull_request]
jobs:
static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run ESLint
run: npx eslint src/ --format json > eslint-report.json
- name: Run SonarQube Analysis
uses: sonarsource/sonarqube-scan-action@master
with:
args: >
-Dsonar.qualitygate.wait=true
- name: Check Security
run: npx snyk test --severity-threshold=highQuality Gates
Define Thresholds:
| Metric | Threshold | Action |
|---|---|---|
| New bugs | 0 | Block merge |
| Security vulnerabilities | 0 critical | Block deploy |
| Code coverage | >80% | Warn |
| Cyclomatic complexity | 15 max per function | Warn |
| Technical debt | 8 hours max new | Inform |
Shift-Left Testing
Catch issues earlier by running analysis:
| Stage | Analysis Type |
|---|---|
| IDE | Real-time linting, basic static analysis |
| Pre-commit | Formatting, linting, simple security checks |
| CI Build | Full static analysis, security scanning |
| Pre-Deploy | Dynamic analysis, performance tests |
| Production | Monitoring, APM, error tracking |
Tool Selection and Best Practices
Selecting the Right Tools
Criteria for Tool Selection:
| Factor | Considerations |
|---|---|
| Language support | Matches your technology stack |
| Integration | Works with IDE, CI/CD, issue trackers |
| False positive rate | Manageable noise level |
| Customization | Rules can be tuned to your needs |
| Team adoption | Easy to use, good documentation |
| Cost | Fits budget (many good free options) |
Managing False Positives
Strategies:
# Strategy 1: Inline suppression (use sparingly)
def legacy_function():
# noqa: C901 - Complex but well-tested legacy code
...
# Strategy 2: Configuration-based exclusion
# .eslintrc.json
{
"rules": {
"no-console": "off" // Allow in dev environment
}
}
# Strategy 3: Baseline approach
# Compare against known state, only flag new issuesBest Practices Summary
- Start with defaults - Use tool defaults before customizing
- Fix incrementally - Don't try to fix everything at once
- Prioritize security - Always address security issues first
- Track trends - Monitor metrics over time, not just snapshots
- Balance thoroughness and speed - Not everything needs maximum analysis
- Combine approaches - Use both static and dynamic analysis
- Educate the team - Ensure everyone understands the tools
- Review regularly - Audit rules and thresholds periodically
Test Your Knowledge
Quiz on CTAL-TTA Static and Dynamic Analysis
Your Score: 0/10
Question: What is the fundamental difference between static analysis and dynamic analysis?
Continue Your Certification Journey
Frequently Asked Questions
Frequently Asked Questions (FAQs) / People Also Ask (PAA)
What is the main difference between static and dynamic analysis?
How do I calculate cyclomatic complexity?
What are common static analysis tools I should know for CTAL-TTA?
What memory issues can dynamic analysis detect that static analysis cannot?
How should I integrate static analysis into a CI/CD pipeline?
What should I look for when reviewing code as a Technical Test Analyst?
What is the difference between a profiler and a memory analyzer?
How do I deal with false positives from static analysis tools?