Fact-checked by Grok 2 weeks ago

Program analysis

Program analysis is a branch of focused on the automatic examination of computer programs to infer properties such as correctness, , , and behavior without necessarily executing the . It serves as a foundational technique for , enabling developers and engineers to detect bugs, optimize , and verify compliance with specifications in complex systems. The field addresses the inherent challenges of software complexity, where manual inspection becomes impractical, by providing scalable methods to analyze at various scales, from individual functions to large-scale distributed applications. At its core, program analysis divides into two primary categories: static analysis, which inspects source or compiled code without running it to extract facts like potential errors or dependencies, and dynamic analysis, which observes behavior through execution traces or to capture actual program states. Static approaches, such as and , offer broad coverage but may produce false positives due to conservative approximations, while dynamic methods provide precise insights at the cost of requiring test inputs and potentially missing unexercised paths. These techniques underpin tools used in compilers for optimization, integrated development environments for feedback, and security scanners for vulnerability detection. The theoretical foundations of program analysis trace back to early results, including Alan Turing's 1936 proof of the undecidability of the , which limits the precision of general analyses, and , establishing that non-trivial properties of program behavior are undecidable. Seminal advancements, like the formalization of by Patrick and Radhia Cousot in 1977, provided a rigorous framework for sound static analyses by mapping concrete program semantics to abstract domains. Today, the field continues to evolve with integrations of for improved precision and applications in emerging domains like and distributed systems.

Introduction

Definition and Goals

Program analysis is the systematic examination of software artifacts, such as , to infer properties about a program's behavior, structure, or performance without necessarily executing it. This field in employs automated techniques to approximate the dynamic behavior of programs, providing reliable insights into how software operates across possible executions. Analyses can occur at different representation levels, including for high-level semantic understanding, for intermediate portability in virtual machines, or for low-level efficiency in deployed systems. The primary goals of program analysis are to detect by identifying potential defects like dereferences, verify program correctness against specifications, optimize code for better performance through techniques such as , ensure security by uncovering vulnerabilities like buffer overflows, and facilitate refactoring by revealing dependencies and structural insights. These objectives support broader , enabling developers to produce reliable and efficient systems. Key properties analyzed include code reachability to determine executable paths, to identify multiple references to the same location, and usage to assess demands on or . Program analysis integrates throughout the lifecycle, from initial design and implementation to testing, maintenance, and evolution, thereby reducing costs and enhancing reliability. Approaches encompass both static methods, which examine code without running it, and dynamic methods, which observe behavior during execution.

Historical Development

The origins of program analysis trace back to the late and early , during the development of early optimizing compilers for high-level programming languages. The I compiler, released by in 1957, incorporated rudimentary flow analysis techniques in its Sections 4 and 5 to determine the frequency of execution for different program paths, enabling basic optimizations such as and dead code removal through a simulation of . This marked one of the first systematic uses of program analysis for optimization, building on prior work in and integrated into the II system for the 7090 in 1961 by Vyssotsky and others. In the 1970s, program analysis advanced significantly with foundational work on data-flow frameworks. introduced a unified method for global in 1973, formalizing as iterative computations over lattices to propagate information across program control structures, proving convergence under certain conditions and enabling optimizations like constant propagation and reaching definitions. Concurrently, at developed key techniques for , including her 1970 framework for analyzing and transforming control-flow graphs, which influenced subsequent optimizations and earned her the 2006 for contributions to theory. The 1980s and 1990s saw the emergence of more formal and scalable approaches, expanding program analysis beyond optimization to . Patrick and Radhia Cousot formalized in 1977 as a unified lattice-based framework for approximating program semantics, allowing sound static analyses for properties like safety and termination by constructing fixpoints in abstract domains. Independently, in the early 1980s, Edmund Clarke and E. Allen Emerson developed for algorithmic verification of finite-state concurrent systems, while Joseph Sifakis and Jean-Pierre Queille advanced similar automata-based techniques; their combined innovations, recognized with the 2007 , enabled exhaustive exploration of state spaces for detecting errors in hardware and software. From the 2000s onward, program analysis integrated into modern toolchains and addressed security challenges, driven by and industry needs for scalability. Chris Lattner's framework, initiated in 2000 and detailed in 2004, provided a modular for lifelong program analysis using , facilitating optimizations and enabling widespread adoption in tools like for static analyses across languages. The 2014 vulnerability in spurred advancements in analysis, a dynamic and static technique for tracking untrusted data flows to prevent memory leaks and injections, with tools like demonstrating its role in detecting similar buffer over-reads through taint propagation from inputs. This period also reflected a shift toward scalable in industry, such as in tools like KLEE (built on ), balancing precision with performance for verifying complex software. In the , program analysis has increasingly incorporated large language models to assist in tasks like static analysis, bug detection, and code understanding, further enhancing automation and precision in .

Classification of Program Analysis

Static versus Dynamic Analysis

Static analysis examines the source code or binary of a program without executing it, enabling reasoning about all possible execution behaviors to pursue exhaustive coverage of the program's state space. This approach is foundational in areas like optimization, where approximations of program semantics are computed to detect errors or optimize ahead of runtime. However, fundamental limits arise from undecidability, as demonstrates that non-trivial semantic properties of —such as whether a program computes a specific —are inherently undecidable, necessitating over-approximations in static analyses that may include infeasible behaviors and lead to false positives. In contrast, dynamic analysis relies on executing the program, either in full or on selected inputs, to gather concrete observations of its , yielding precise results for the paths actually traversed. This execution-based method excels in revealing real-world issues like performance bottlenecks or specific defects under given conditions but is inherently limited by incomplete path coverage, as the of possible execution paths—often termed path explosion—renders exhaustive testing impractical even for modest programs. The primary trade-offs between these approaches center on , , and . Sound static analyses guarantee detection of all errors (no false negatives) by over-approximating possible behaviors, though this conservatism reduces and increases false positives, while requiring substantial computational resources for . Dynamic analyses, conversely, offer high with no false positives for observed executions—since results stem directly from runs—but sacrifice due to potential misses in unexplored paths, though they are typically more efficient per . These complementary strengths have spurred hybrid techniques, such as concolic execution, which integrate execution with symbolic reasoning to balance coverage and without fully resolving the underlying challenges.

Soundness, Completeness, and Precision

In program analysis, soundness, completeness, and precision are fundamental properties that assess the reliability and accuracy of an analysis's results relative to the program's actual semantics, often formalized using denotational semantics where the concrete semantics denotes the set of all possible program behaviors. Soundness ensures that the analysis captures all true behaviors of the program, producing no false negatives; formally, if the concrete semantics of a program is the set of all reachable states or behaviors S, a sound analysis yields an over-approximation \hat{S} such that S \subseteq \hat{S}. This property is crucial for verification tasks, as it guarantees that the absence of reported issues in the analysis implies no issues exist in reality. In the framework of abstract interpretation, soundness is established via a pair of abstraction function \alpha and concretization function \gamma satisfying \gamma(\alpha(x)) \sqsupseteq x for concrete values x, ensuring the abstract domain correctly approximates the concrete one without omission. Completeness requires that the analysis results match the true behaviors exactly, with \hat{S} = S, eliminating both false negatives and false positives; in terms, this holds when \alpha(\gamma(y)) = y for abstract values y, meaning the approximation introduces no extraneous information. However, is generally undecidable for nontrivial program properties due to the implications of the , which shows that determining exact semantic properties like termination or reachability is impossible in finite time for Turing-complete languages. Precision measures the tightness of the , quantifying how closely \hat{S} adheres to S by minimizing spurious elements in over-approximations; for instance, it can be assessed via the width or of the abstract domain relative to the one, where narrower domains yield finer-grained results. Achieving high often involves trade-offs with , as more precise analyses require computationally intensive operations like narrowing to refine approximations, potentially increasing analysis time exponentially. These properties manifest differently across analysis types: static analyses typically employ over-approximation to ensure by including all possible behaviors (e.g., "may" analyses that report potentially reachable states), while dynamic analyses often use under-approximation, capturing only observed behaviors during execution (e.g., "must" analyses confirming definite properties in tested paths). This distinction aligns with the broader static-dynamic divide, where static methods prioritize exhaustive coverage at the cost of potential imprecision.

Static Program Analysis Techniques

Control-Flow Analysis

Control-flow analysis is a fundamental static technique in program analysis that models the possible execution paths of a program by constructing a (CFG). A CFG represents the program as a where nodes correspond to basic blocks—maximal sequences of straight-line code without branches or jumps—and edges indicate possible control transfers, such as conditional branches, unconditional jumps, loops, or procedure returns. This graph-based representation, pioneered in early optimization work, enables the identification of structured control elements like sequences, selections, and iterations without simulating execution. The construction of CFGs typically proceeds intraprocedurally, focusing on the control structure within a single or . Basic blocks are identified by partitioning the code at entry points, branch targets, and control-altering statements, with edges added based on the semantics of jumps, calls, and . For interprocedural analysis, which extends the CFG across procedure boundaries, additional edges connect call sites to entry nodes of callees and return sites to post-call nodes, often modeling the call graph to handle procedure interactions. However, precise interprocedural CFGs require approximations to avoid , such as treating calls as simple transfers or using call-string summaries for context sensitivity. Once constructed, CFGs facilitate computations of structural properties essential for further . Dominators are nodes through which every path from the entry must pass to reach a given , computed efficiently using iterative algorithms that propagate dominance information forward from the entry; a simple, fast method iterates over a post-order traversal until fixed-point , achieving near-linear time in practice. Post-dominators are defined analogously but backward from the . These concepts underpin loop detection: natural loops are identified by back edges in a , where a back edge from m to n defines a loop with header n and body consisting of all nodes that can reach m without passing through n, enabling optimizations like . Introductory applications of CFGs involve solving basic data-flow problems to illustrate control . Reaching determines, for each program point, the set of assignments that may reach it along some from the entry, formulated as a forward problem where definitions "gen" at sites and are "kill"ed by subsequent redefinitions; solutions are computed via iterative over the CFG edges until fixed point. Similarly, live identifies used before their next definition along any to the , a backward problem where uses "gen" at read sites and definitions "kill" liveness, aiding by revealing lifetimes. These computations highlight the CFG's role as a prerequisite for propagating information in more advanced analyses. Despite their utility, CFGs face limitations in handling complex control features. Recursion introduces cycles in the interprocedural , potentially leading to infinite paths that require summarization or bounded call-string approximations to terminate . Exceptions and non-local transfers, such as those in languages like or C++, add irregular edges from throw sites to handlers, complicating graph construction and often necessitating separate exception-flow graphs or integrated modeling to capture all paths. Scalability issues arise in large codebases, where interprocedural CFGs can explode in size due to call-site multiplicity, though practical implementations mitigate this with on-demand construction and pointer analysis for call resolution, maintaining efficiency for programs up to millions of lines.

Data-Flow Analysis

Data-flow analysis is a framework for statically gathering information about the possible values of variables or expressions at various points in a program by propagating facts along paths in the . This technique enables the inference of program properties such as which definitions reach a use or which expressions are available, facilitating optimizations like and . Originating from early optimization efforts, it models data dependencies abstractly to avoid the undecidability of precise value tracking. The core framework employs a structure to represent sets of facts at program points, where the (L, \sqcap, \sqcup, \bot, \top) defines the possible states, with \sqcap as the meet (join) operation for combining information at control-flow merges. Transfer functions f: L \to L describe how facts transform through statements and must be monotonic, preserving the partial order: if x \sqsubseteq y, then f(x) \sqsubseteq f(y). Analyses are solved via , computing the least fixed point of the data-flow equations over the , which converges in finite steps due to the 's finiteness. The worklist implements this efficiently by maintaining a queue of nodes whose information needs updating, propagating changes until stabilization. Analyses are classified as forward or backward. Forward analyses, like reaching definitions, propagate information from program entry toward exit, while backward analyses, like available expressions, flow from exit toward entry. For a program point p, the basic equations are: \text{IN} = \bigsqcap_{q \in \text{pred}(p)} \text{OUT} \text{OUT} = f_p(\text{IN}) where f_p is the for p, and \bigsqcap is the meet over predecessors at join points. Classic problems illustrate the framework. Reaching definitions determines which variable assignments may reach a point, using a forward analysis with (\sqcup = \cup) as the meet and sets of definitions as the . The equations are: \text{IN} = \bigcup_{q \in \text{pred}(p)} \text{OUT} \text{OUT} = \text{gen} \cup (\text{IN} \setminus \text{kill}) where \text{gen} are definitions generated at p, and \text{kill} are those invalidated. Available expressions, a backward analysis for , tracks expressions computable at a point from entry, using (\sqcap = \cap) and analogous gen/kill sets in reverse. Constant propagation forwards constant values across assignments, employing a of constants (e.g., \bot for unknown, specific values, or \top for non-constant), with transfer functions substituting constants where possible. Extensions address limitations in larger programs. Flow-insensitive analyses approximate by treating statement order loosely, often via fixed-point over all statements, to reduce at the cost of precision. Context-sensitive variants, crucial for interprocedural analysis, distinguish call sites by modeling calling contexts (e.g., via call strings or graph reachability), enabling precise propagation across procedures while maintaining polynomial-time solvability for distributive problems. These apply to by identifying unreachable or unused code through reaching definitions or live variables, removing it to reduce program size and execution time.

Abstract Interpretation

Abstract interpretation provides a general framework for the static analysis of programs by approximating their semantics in a way that ensures decidability and . Introduced by Patrick and Radhia Cousot, it formalizes program analysis as the of programs within domains that over-approximate the concrete semantics of the program. This approach allows for the automatic determination of program properties by computing fixpoints in these abstract domains, which are typically lattices equipped with operations that mimic but simplify the concrete semantics. At the core of lies the theory of s between a domain \langle \mathcal{C}, \sqsubseteq \rangle representing the exact program semantics and an abstract domain \langle \mathcal{A}, \sqsubseteq^\sharp \rangle providing approximations. A is defined by a pair of monotonic functions \alpha: \mathcal{C} \to \mathcal{A} () and \gamma: \mathcal{A} \to \mathcal{C} (concretization) such that for all c \in \mathcal{C} and a \in \mathcal{A}, \alpha(c) \sqsubseteq^\sharp a c \sqsubseteq \gamma(a). is ensured because the abstract domain over-approximates the concrete one: \gamma(\alpha(c)) \sqsupseteq c for all c \in \mathcal{C}, meaning that any property proven in the abstract domain holds in the semantics, though the converse may not be true due to potential false positives. The process involves defining an abstract semantics \llbracket \cdot \rrbracket^\sharp that collects the effects of program statements in \mathcal{A}, often by interpreting the program's in the abstract domain. For terminating computations, this is straightforward, but for loops and , termination is achieved using widening operators \nabla: \mathcal{A} \times \mathcal{A} \to \mathcal{A}, which are monotonic functions satisfying x \sqsubseteq^\sharp \nabla(x, y) and y \sqsubseteq^\sharp \nabla(x, y), ensuring that iterative applications stabilize after finitely many steps to compute an over-approximation of the least fixpoint, often denoted \mathsf{T}^\omega. Practical examples illustrate the framework's application. In interval analysis, the abstract domain consists of intervals [\ell, u] for numerical variables, where \alpha maps a set of concrete values to the tightest enclosing interval, and operations like addition are defined component-wise with appropriate handling for overflow or underflow. For instance, analyzing x = x + 1 in a loop might yield an interval that widens to [-\infty, +\infty] after iterations, proving boundedness or detecting potential overflows conservatively. Sign analysis uses a simpler domain \{ -, 0, +, \top \}, where \top represents unknown signs, and abstract operations propagate sign information through assignments and conditions, effectively handling non-determinism by merging possibilities into the least upper bound in the lattice. Non-determinism in inputs or control flow is managed by the join operation \sqcup^\sharp, which computes the least upper bound in \mathcal{A}, ensuring the analysis remains sound by including all possible behaviors. The advantages of include its modular design, allowing analysts to define new abstract domains and operations tailored to specific properties without altering the underlying framework, and its deep connection to , where the concrete semantics serves as a semantic function that is lifted to the abstract level. This modularity facilitates the development of analyses for diverse properties, from numerical accuracy to resource usage. can be viewed as a special case of abstract interpretation using finite-height domains without explicit widening.

Type Systems

Type systems provide a static analysis mechanism in programming languages to assign types to variables, expressions, and functions, ensuring that operations are applied only to compatible data structures. This assignment is formally defined through typing rules, such as \Gamma \vdash e : \tau, where \Gamma is a typing environment mapping variables to types, e is an expression, and \tau is the inferred type of e. These rules specify how types propagate through program constructs, for example, ensuring that the type of an application matches the function's parameter type. Seminal work on , particularly the Hindley-Milner algorithm, enables automatic derivation of principal types for polymorphic functions in languages like , achieving completeness for a decidable fragment of the with polymorphism. Static type checking uses these systems to detect type errors before program execution, preventing mismatches such as adding a string to an integer. Polymorphism enhances flexibility: parametric polymorphism allows functions like map to work uniformly across types without explicit instantiation, as in the Hindley-Milner system, while ad-hoc polymorphism, often via type classes or overloading, provides type-specific implementations, such as numeric operations on integers versus floats. Subtyping further refines this by permitting a type \tau_1 to be safely substituted for \tau_2 if \tau_1 \leq \tau_2, enabling structural compatibility in object-oriented languages, as formalized in structural subtyping rules. Advanced type systems extend these foundations to express richer properties. Dependent types allow types to depend on values, enabling specifications like array access within bounds (e.g., indexing an array of length n only at positions from 0 to n-1), as demonstrated in practical extensions to ML-like languages. Flow-sensitive refines by tracking type changes along control-flow paths, such as refining a variable's type after a conditional , improving over flow-insensitive approximations. Type systems can be viewed as interpretations over type lattices, specializing the general framework to safety properties. Despite their power, type systems face trade-offs between expressiveness and decidability; ensuring type checking remains computable limits their ability to verify arbitrary properties, such as catching all runtime errors like in general-purpose languages. For instance, full dependent types can encode undecidable propositions, necessitating restrictions for practical inference.

Model Checking

Model checking is an automated verification technique that exhaustively determines whether a finite-state model of a system satisfies a given specification, typically expressed in temporal logic. The models are commonly represented as Kripke structures, consisting of a set of states S, a transition relation R \subseteq S \times S, an initial state I \subseteq S, and a labeling function L: S \to 2^{AP} that assigns subsets of atomic propositions AP to each state. Specifications are formulated in logics such as Linear Temporal Logic (LTL) for linear-time properties or Computation Tree Logic (CTL) for branching-time properties; for instance, the CTL formula AG \, p \rightarrow AF \, q asserts that whenever proposition p holds invariantly from the initial state, proposition q will eventually hold along some path. If the property fails, model checkers generate a counterexample trace—a finite path in the model violating the specification—to aid debugging. Core algorithms operate on automata-theoretic or symbolic representations to handle the verification. For LTL specifications, an automata-based approach constructs a Büchi automaton from the negation of the formula and checks the emptiness of the product automaton with the Kripke structure model. In software contexts, models are often expressed as Labelled Transition Systems (LTS), where transitions are labeled with actions, enabling the analysis of concurrent behaviors. Symbolic model checking, pioneered using Binary Decision Diagrams (BDDs), represents the state space implicitly to avoid explicit enumeration, as implemented in early tools like for CTL formulas. To combat state explosion—the exponential growth in the number of states—techniques such as model minimization reduce equivalent states, while partial order reduction prunes the exploration by ignoring independent concurrent actions that do not affect the property. Originally developed for in the early 1980s, has been extended to software systems, where finite-state abstractions of programs are checked against temporal properties. Seminal work by Clarke and Emerson introduced CTL model checking algorithms for branching-time logic in , demonstrating polynomial-time verification relative to the model size. Tools like , first applied to circuit designs, influenced software extensions by providing counterexample-guided diagnostics. Scalability remains a primary challenge due to the state explosion in concurrent systems, often addressed through abstraction-refinement loops like Counterexample-Guided Refinement (CEGAR), which iteratively refines over-approximate abstract models upon discovering spurious counterexamples. This process ensures soundness by guaranteeing that valid counterexamples in the concrete model are preserved, though it requires careful selection for effective refinement.

Dynamic Program Analysis Techniques

Testing

Testing is a fundamental dynamic program analysis technique that involves executing a with selected inputs to observe its and verify whether it meets specified requirements. By running the software in a controlled , testing aims to uncover defects, ensure functionality, and assess reliability under various conditions. Unlike static analysis, which examines without execution, testing relies on actual to detect issues such as crashes, incorrect outputs, or performance anomalies. This approach is essential in lifecycles, where it supports iterative validation from early stages to final deployment. Testing occurs at multiple levels to address different scopes of the system. focuses on individual components or functions in isolation, verifying their internal logic against expected behaviors. examines interactions between units, ensuring that combined modules function correctly without introducing new faults. evaluates the entire integrated application against overall requirements, often simulating real-world usage scenarios. These levels build progressively, with unit tests providing foundational confidence before broader integration and system validation. Testing strategies are broadly categorized as or , differing in their knowledge of internal structure. treats the program as a opaque entity, selecting inputs based on external specifications and checking outputs for correctness without examining code paths. In contrast, leverages knowledge of the program's internals to design tests that exercise specific structures, guided by coverage criteria such as statement coverage (executing every line), branch coverage (traversing all decision outcomes), or path coverage (exploring feasible execution paths). For safety-critical systems like , (MC/DC) is mandated, requiring each condition in a decision to independently affect the outcome, ensuring thorough fault detection in expressions. The testing process begins with test case generation, which can be manual, random, or model-based. Manual generation involves developers crafting inputs based on and requirements, allowing targeted exploration of cases but prone to human bias and incompleteness. Random testing automates input selection from input domains, often using feedback to refine sequences, as in tools like Randoop for , which generates unit tests by randomly composing method calls and pruning invalid ones. Model-based approaches derive tests from formal models of the system's behavior, such as state machines or UML diagrams, enabling systematic coverage of transitions and states. During execution, assertions embedded in code check invariants, while test oracles determine pass/fail by comparing actual outputs to expected results, often derived from specifications or historical data. Recent advancements include techniques for automated test generation, improving coverage and fault detection in complex systems as of 2024. Effectiveness of test suites is measured using metrics like and . Coverage metrics quantify how much of the code is exercised; for instance, branch coverage above 80% is often a for adequate testing in industrial practices, though full path coverage is computationally infeasible for complex programs due to exponential paths. assesses suite quality by injecting small faults () into the code and verifying if tests detect them, with mutation scores indicating fault-detection capability; empirical studies have shown correlations between mutant detection and real fault revelation. Static path analysis can aid by identifying feasible paths for targeted test generation. Despite its strengths, testing has inherent limitations. It cannot prove the absence of , as exhaustive testing is impractical for non-trivial programs, potentially missing defects in unexercised paths. The oracle problem exacerbates this for non-deterministic code, where outputs vary due to timing, concurrency, or external factors, making expected results hard to define without complete specifications. Thus, testing complements but does not replace other methods.

Runtime Monitoring

Runtime monitoring is a dynamic program analysis technique that observes and analyzes the execution of a program in real-time or post-execution to detect violations of specified properties, gather performance profiles, or identify anomalies during runtime. Unlike static analysis, which examines code without execution, runtime monitoring relies on actual program runs to collect data such as variable states, control flows, and system events, enabling the verification of behaviors that may be infeasible to predict statically. This approach is particularly valuable for systems where non-determinism or environmental interactions make exhaustive static prediction challenging. Key mechanisms for include , which inserts probes or code snippets into the program to capture execution events, and event for recording traces that can be analyzed later. Probes can be added at compile-time, load-time, or , often using techniques like binary tools (e.g., Pin or ) to avoid modifications. (AOP) provides a modular way to implement such by defining aspects that weave logic around specific join points, such as method calls or exceptions, without altering the core program structure. Event involves capturing sequences of events into traces, which are then processed for pattern matching or statistical analysis to infer system behavior. Applications of runtime monitoring span several areas, including invariant checking, where tools like dynamically infer and verify likely program invariants—such as range constraints or relational properties—by observing multiple executions and reporting those holding true across traces. For performance profiling, generates call graphs and execution time breakdowns by instrumenting functions to count calls and sample runtime, helping identify bottlenecks in large programs. Anomaly detection uses monitoring to flag deviations from expected patterns, such as unusual resource usage or security violations, often in for immediate response. Modern applications include monitoring in and distributed systems, leveraging for anomaly prediction as of 2024. Runtime monitoring techniques distinguish between online analysis, which processes events as they occur to enable immediate feedback or enforcement (e.g., halting execution on violation), and offline analysis, which examines complete traces after execution for comprehensive post-mortem insights. Online monitoring suits time-sensitive applications like safety-critical systems, while offline allows deeper analysis without runtime constraints. Handling concurrency requires thread-safe monitors that synchronize access to shared state, often using atomic operations or lock-free data structures to avoid race conditions in multi-threaded environments. Significant challenges in runtime monitoring include minimizing overhead, as instrumentation can increase execution time by 10-50% or more depending on probe density and analysis complexity, prompting optimizations like selective sampling or hardware-assisted tracing. Non-determinism in traces, arising from concurrent scheduling, external inputs, or timing variations, complicates by producing variable execution paths, necessitating robust trace merging or probabilistic models to ensure reliable detection.

Program Slicing

Program slicing is a technique for reducing a program or its execution to a of statements that are relevant to a specific criterion, such as the value of a particular variable at a given program point. Introduced by in 1981, static program slicing operates on the source without executing it, producing a slice that includes all statements that may potentially affect the criterion across all possible executions. In contrast, dynamic program slicing, proposed by Bogdan Korel and Janusz Laski in 1988, focuses on a specific execution for given inputs, yielding a slice that captures only the statements that actually influence the criterion during that run. These approaches leverage program dependencies to isolate relevant computations, aiding in tasks like fault localization by eliminating irrelevant . The core algorithms for computing slices rely on dependence graphs that model and flows in the program. A key representation is the program dependence graph (PDG), developed by Ferrante, Ottenstein, and Warren in 1987, which combines data dependences (how values flow between statements) and dependences (how execution paths influence statement ) into a single directed graph. In backward slicing, the most common variant for , traversal starts from the slicing criterion and proceeds upstream through the dependence graph to include all predecessor statements that could impact it. Forward slicing, conversely, begins at a starting point and collects all downstream effects, useful for impact analysis. These graph-based methods ensure the slice preserves the semantics relevant to the criterion while removing extraneous parts. A slicing criterion typically specifies a variable v and a program point (e.g., line l), aiming to extract "all statements affecting v at l". For static slicing, algorithms like those using reaching definitions and live variables from data-flow analysis compute the slice by propagating dependencies across the entire control-flow graph. In dynamic slicing, computation involves an execution history or trace, where statements are filtered based on actual data flows during runtime; for instance, only executed paths contributing to the variable's value are retained, often using dynamic dependence graphs or post-execution marking of relevant instructions. This results in an executable subset that reproduces the criterion's behavior for the specific input. Program slicing finds primary applications in , where slices help isolate faults by focusing on code paths leading to erroneous values, as originally envisioned by Weiser. In , dynamic slices reduce test suites by selecting only relevant execution traces, improving efficiency without sacrificing coverage of critical behaviors. Additionally, slices enhance program comprehension by abstracting complex codebases to manageable views, facilitating maintenance and understanding of how changes propagate.

Example

Consider a in :
1: input x
2: y = x + 1
3: if x > 0 then
4:   z = y * 2
5: else
6:   z = y
7: endif
8: output z
A static backward slice for criterion \langle z, 8 \rangle includes lines 1–8, as all paths may affect z. For input x = 1, a dynamic slice might reduce to lines 1, 2, 3, 4, 7, 8, excluding the else branch.

Applications

Compiler Optimization

Compiler optimization leverages program analysis techniques to transform source code or intermediate representations into more efficient machine code, primarily aiming to minimize execution time, reduce code size, and lower resource usage while preserving program semantics. Static analyses, such as data-flow analysis, play a central role in identifying opportunities for transformations like dead code elimination, where unreachable or unused code is removed based on reaching definitions or liveness information. Constant folding evaluates and replaces constant expressions at compile time, simplifying computations that do not depend on runtime values. These intra-procedural optimizations rely on control-flow and data-flow frameworks to propagate information across basic blocks, enabling safe code removal or simplification without altering behavior. In the compiler pipeline, optimization occurs across multiple phases, starting in the front-end during semantic analysis to eliminate early redundancies, progressing to the middle-end for machine-independent transformations, and culminating in the back-end for architecture-specific adjustments. For instance, identifies computations within loops that yield the same value on every iteration and hoists them outside, reducing redundant operations; this is guided by data dependence analysis to ensure correctness. Register allocation in the back-end employs liveness analysis—a backward data-flow problem—to determine variable lifetimes, minimizing spills to and optimizing usage for faster execution. Interprocedural analyses extend these to whole programs, constructing call graphs to enable inlining, where small or frequently called functions are merged into callers, eliminating call overhead and exposing further optimizations. Advanced optimizations like transform scalar loops into vector instructions for SIMD architectures, requiring precise dependence analysis to detect absence of data dependencies between loop iterations. This involves testing for flow, anti-, and output dependencies using techniques such as Banerjee's inequalities, which bound possible dependence distances to confirm parallelizability. In just-in-time () compilers, complements static methods by execution at runtime to identify hot paths and apply targeted optimizations, such as speculative inlining based on observed call frequencies. For example, implementations use counter-based to reorder code layout or specialize hot methods, adapting to actual workloads. These optimizations yield measurable benefits, with studies showing average speedups of 11% to 33% across GPU architectures through refined phase ordering, alongside code size reductions of up to 50% in selective sequences on large software packages. However, they introduce trade-offs, as aggressive analyses increase compilation time—sometimes by factors of 2-5—and may complicate debugging due to transformed code structures. Overall, the integration of static and dynamic analyses in modern compilers, such as and , balances these factors to achieve substantial performance gains in production workloads.

Software Verification and Debugging

Software verification and debugging leverage program analysis techniques to ensure correctness and identify defects during software development. These methods systematically examine code or its execution to prove properties like safety invariants or locate faults, reducing reliance on manual inspection and enhancing reliability in complex systems. Formal verification approaches, such as , exhaustively explore state spaces to confirm that software satisfies specifications, while static analysis tools detect common errors like dereferences without running the program. In debugging, dynamic techniques trace data flows to pinpoint root causes, enabling developers to isolate issues efficiently. Integration of these analyses into development workflows further streamlines the process, from feedback in editors to automated checks in build pipelines. In , formal methods like play a central role by automatically verifying that finite-state systems adhere to invariants expressed in . Pioneered in the , this technique constructs a model of the program and explores all possible executions to check for violations of safety or liveness properties, providing counterexamples if failures occur. For instance, it has been applied to concurrent systems to ensure freedom or . Complementing this, static checkers perform lightweight analyses on to identify potential defects; the FindBugs tool, for example, uses pattern-based detection to flag null pointer dereferences by tracking possible null values through control and data flows. These tools operate interprocedurally, propagating annotations to refine precision and reduce false positives, making them suitable for early-stage verification in large codebases. Debugging benefits from program analysis by narrowing the search space for faults. Program slicing, which extracts subsets of code relevant to a specific criterion like a or , aids fault localization by highlighting dependencies that could contribute to errors; techniques detailed elsewhere refine this for dynamic executions to rank suspicious statements. Dynamic taint tracking extends this by marking sensitive data at inputs and propagating taints through execution, revealing root causes such as unintended data influences leading to crashes or incorrect outputs. supports memory-related debugging by determining if objects allocated locally remain confined to a or , helping identify leaks or issues without full execution traces. Tool integration embeds these analyses into everyday workflows for seamless verification and debugging. IDE plugins, such as SonarLint, provide on-the-fly static analysis within editors like or , flagging issues like null dereferences as developers write code and suggesting fixes based on rule sets. In pipelines, tools like automate verification by scanning builds for defects, enforcing quality gates before deployment and integrating with platforms such as Jenkins or GitHub Actions to halt faulty commits. For safety-critical domains, specialized integrations like LDRA's suite align analyses with standards, running coverage and static checks in automated workflows to verify compliance. A notable in illustrates the impact of program analysis under standards, which mandate rigorous for airborne software to achieve design assurance levels from A () to E (no effect). In developing flight control systems, employed structural coverage analysis to verify and detect unexercised paths, ensuring high coverage for DAL A while reducing manual review efforts. Similarly, a federal agency used Parasoft's unified testing platform, incorporating static analysis and unit , to comply with for software, cutting testing time by automating defect detection and evidence generation. These applications demonstrate how analysis techniques mitigate risks in high-stakes environments, fostering safer software through proven, standards-aligned processes.

Security Analysis

Program analysis plays a crucial role in by identifying vulnerabilities that could lead to exploits, such as injection attacks or corruptions, through techniques that track flows and transfers without executing the code. Static taint analysis, for instance, propagates labels on potentially malicious inputs to detect flaws like (SQLi), where untrusted reaches query construction points. In this approach, from external sources is marked as tainted and tracked through the program's and flow graphs to identify unsafe uses, such as direct into SQL statements. A seminal implementation demonstrated its effectiveness in applications by achieving high precision in detecting SQLi alongside other taint-style vulnerabilities, reducing false positives through context-sensitive slicing. Dynamic symbolic execution complements static methods by exploring program paths with symbolic inputs during runtime, enabling the detection of buffer overflows where array bounds are exceeded due to unchecked inputs. This technique solves path constraints using satisfiability modulo theories (SMT) solvers to generate concrete inputs that trigger vulnerabilities, often uncovering deep bugs missed by traditional testing. For example, tools like KLEE apply dynamic symbolic execution to C programs, automatically generating tests that expose buffer overflows by systematically enumerating feasible execution paths and verifying assertions on memory accesses. Such analysis has proven effective in complex systems, achieving over 80% code coverage in benchmarks while identifying real-world memory errors. Key security properties enforced via program analysis include information flow control, which ensures non-interference by preventing sensitive data from influencing public outputs, and (CFI), which restricts indirect jumps to valid targets to thwart (ROP) attacks. Non-interference, formalized as a property where high-security inputs do not affect low-security observations, is verified through flow-sensitive analyses that model dependencies across program states. , in contrast, instruments binaries to check that control transfers align with a precomputed , mitigating ROP chains that repurpose existing code gadgets. The original CFI framework reduced exploit success rates to near zero in evaluated scenarios while incurring modest runtime overhead of 8-16%. Practical tools exemplify these techniques: Fortify performs static analysis to scan for leading to injections and other issues, supporting languages like and C# with customizable rulesets for enterprise security. , a dynamic framework, detects memory leaks and overflows at by shadowing allocations and validating accesses, commonly used in C/C++ to prevent exploitation vectors like use-after-free. In web applications, static analysis detects (XSS) by modeling string operations and sink points like script insertions, with one approach identifying reflected XSS through value-flow tracking in code, achieving 92% precision on benchmarks. Post-2020, program analysis has increasingly addressed supply-chain attacks, as exemplified by the incident where was injected into software updates, compromising thousands of organizations. Analyses of such breaches employ binary instrumentation and dependency scanning to verify build integrity and detect tampered artifacts, emphasizing software bill of materials (SBOM) generation for tracking. Concurrently, AI-assisted methods, particularly large language models (LLMs) integrated with static analyzers, enhance detection by reasoning over code repositories to identify subtle patterns, outperforming traditional tools in recall for zero-day issues while reducing manual review efforts. techniques, detailed elsewhere, further support secure information flows in these contexts.

References

  1. [1]
    A Survey of Program Analysis for Distributed Software Systems
    Jul 11, 2025 · As a primary form of software analysis, program analysis is an automatic technique or process of analyzing the behaviors hence properties (e.g., ...
  2. [2]
    Static Analysis: An Introduction - ACM Queue
    Sep 16, 2021 · One such category of tool, static program analysis, consists of programs or algorithms designed to extract facts from another program's source ...
  3. [3]
    [PDF] A Survey of Static Program Analysis Techniques
    Oct 18, 2005 · Computer program analysis is the process of automatically analysing the bahavior of computer programs. There are two main approaches in progam ...
  4. [4]
    Abstract interpretation: a unified lattice model for static analysis of ...
    Abstract interpretation: a unified lattice model for static analysis of programs by construction or approximation of fixpoints. Authors: Patrick Cousot.
  5. [5]
    [PDF] Lecture 1: Introduction to Program Analysis
    Jan 17, 2023 · ○ Program analysis is the systematic examination of a program to ... ○ Program analysis is the systematic examination of a program to.
  6. [6]
    Principles of Program Analysis: | Guide books | ACM Digital Library
    This book is unique in providing an overview of the four major approaches to program analysis: data flow analysis, constraint-based analysis, abstract ...Missing: goals | Show results with:goals
  7. [7]
    [PDF] Notes on Program Analysis
    Program analysis is essential to enforcing security, achieving correctness, improving performance, and reducing costs when developing and maintaining ...
  8. [8]
    Program Analysis - an overview | ScienceDirect Topics
    Program analysis encompasses a set of techniques used to automatically analyze computer software to infer properties about its behavior. These techniques ...
  9. [9]
    A technological review of the FORTRAN I compiler
    FLOW ANALYSIS. The function of Sections 4 and 5 of the compiler was to assign ... Section 4 of the compiler did a flow anlaysis of the program to determine the.
  10. [10]
    [PDF] Compiler-Based Code-Improvement Techniques
    Vyssotsky built control-flow analysis and data-flow analysis into a Fortran II system for the ibm 7090 in 1961 [129]. More formal. Limited distribution for COMP ...
  11. [11]
    Frances Allen - IBM
    An optimizing compiler can be used to minimize or maximize attributes of an executable computer program, including execution time, memory footprint, storage ...
  12. [12]
    Frances Allen - ACM Awards
    Allen's 1966 paper, "Program Optimization," laid the conceptual basis for ... Allen developed and implemented her methods as part of compilers for the IBM STRETCH ...
  13. [13]
    Using Static Analysis and Clang To Find Heartbleed
    Apr 27, 2014 · One approach to identify Heartbleed statically was proposed by Coverity recently, which is to taint the return values of calls to ntohl and ntohs as input data.
  14. [14]
  15. [15]
    [PDF] Static and dynamic analysis: synergy and duality - Computer Science
    Static analysis examines code and all possible behaviors, while dynamic analysis observes program executions, but may not generalize to all possible executions.Missing: trade- offs
  16. [16]
    A Systematic Review of Search Strategies in Dynamic Symbolic ...
    The most challenging problem in DSE is the path explosion, which is inherited from the traditional symbolic execution. By increasing the number of branches, the ...
  17. [17]
    DART: directed automated random testing - ACM Digital Library
    We present a new tool, named DART, for automatically testing software that combines three main techniques.Missing: concolic | Show results with:concolic
  18. [18]
    Systematic design of program analysis frameworks
    Several recent papers (among others Cousot & Cousot[77a], Graham & Wegman[76] ... Systematic design of program analysis frameworks. Theory of computation.
  19. [19]
    [PDF] Reasoning About the Unknown in Static Analysis - Stanford CS Theory
    Hence, a key challenge for static analysis techniques is achieving a satisfactory com- bination of precision, soundness, and scalability by reporting as few ...
  20. [20]
    [PDF] Towards a Unifying Framework for Tuning Analysis Precision by ...
    Static analysis computes an over-approximation of the program semantics, while dynamic analysis under-approximates program semantics. In both cases, we have ...
  21. [21]
    [PDF] Control Flow Analysis
    Having established entry and exit nodes, we can now define the dominance relationships which exist in a directed graph and are of interest in control flow ...
  22. [22]
    Control flow analysis | Proceedings of a symposium on Compiler ...
    Any static, global analysis of the expression and data relationships in a program requires a knowledge of the control flow of the program.
  23. [23]
    [PDF] spa.pdf - Static Program Analysis
    Apr 29, 2025 · a closely related program analysis mechanism called context sensitivity. ... 6887 of Lecture Notes in Computer Science, pages 60–76. Springer ...
  24. [24]
    [PDF] Lecture Notes: Interprocedural Analysis
    We add additional edges to the control flow graph. For every call to function g, we add an edge from the call site to the first instruction of g, and from every ...
  25. [25]
    [PDF] A Simple, Fast Dominance Algorithm
    In this paper, we have presented a technique for computing dominators on a control-flow graph. The algorithm builds on the well-developed and well-understood ...
  26. [26]
    [PDF] Exception Analysis and Points-to Analysis: Better Together
    Exceptions change the control flow of a pro- gram, so they enable or disable object assignments. Furthermore, throwing an exception directly introduces an ...
  27. [27]
    A program data flow analysis procedure | Communications of the ACM
    The global data relationships in a program can be exposed and codified by the static analysis methods described in this paper. A procedure is given which ...
  28. [28]
    A unified approach to global program optimization
    A technique is presented for global analysis of program structure in order to perform compile time optimization of object code generated for expressions.
  29. [29]
    Precise interprocedural dataflow analysis via graph reachability
    The paper shows how a large class of interprocedural dataflow-analysis problems can be solved precisely in polynomial time by transforming them into a special ...
  30. [30]
    P. Cousot & R. Cousot, Abstract interpretation: a unified lattice model ...
    Jan 5, 2010 · Abstract interpretation of programs consists in using that denotation to describe computations in another universe of abstract objects.
  31. [31]
    P. Cousot & R. Cousot, Abstract interpretation and application to ...
    May 4, 2012 · Abstract: Abstract interpretation is a theory of semantics approximation which is used for the construction of semantics-based program analysis ...
  32. [32]
    [PDF] Dynamic interval analysis by abstract interpretation
    ⟨℘(Sℎ+), ⊆⟩. We have provided the example of interval arithmetics. ... Cousot, P., Cousot, R.: Abstract interpretation: A unified lattice model for static.
  33. [33]
    Abstract Interpretation in a Nutshell
    Abstract interpretation consists in considering an abstract semantics, that is a superset of the concrete program semantics.
  34. [34]
    [PDF] A Theory of Type Polymorphism in Programming
    The aim of this work is largely a practical one. A widely employed style of programming, particularly in structure-processing languages.
  35. [35]
    [PDF] Principal type-schemes for functional programs - People @EECS
    Principal type-schemes for functional programs. Luis Damas* and Robin Milner. Edinburgh University. 1. Introduction. This paper is concerned with the ...
  36. [36]
    Principal type-schemes for functional programs - ACM Digital Library
    Principal type schemes for modular programs​​ Two of the most prominent features of ML are its expressive module system and its support for Damas-Milner type ...
  37. [37]
    [PDF] On Understanding Types, Data Abstraction, and Polymorphism
    Parametric polymorphism is obtained when a function works uniformly on a range of types: these types normally exhibit some common structure. Ad-hoc polymorphism ...
  38. [38]
    [PDF] Structural Subtyping and the Notion of Power Type - Luca Cardelli
    The purpose of this paper is to present a type system where subtyping is an orthogonal concept that applies to all type constructions, including function ...
  39. [39]
    [PDF] Dependent Types in Practical Programming
    We present an approach to enriching the type system of ML with a restricted form of dependent types, where type index objects are drawn from a constraint ...
  40. [40]
    [PDF] Flow-Sensitive Type Qualifiers - Stanford CS Theory
    The flow-sensitive analysis associates a store C with each program point. This is in contrast to the flow-insensitive step, which uses one global store CI ...
  41. [41]
    [PDF] Dependent Types and Program Equivalence - andrew.cmu.ed
    In both cases, decidable type checking comes at a cost, in terms of complexity and expressiveness. Conversely, the benefits to be gained by decidable type ...
  42. [42]
    [PDF] Type Systems
    1 Introduction. The fundamental purpose of a type system is to prevent the occurrence of execution er- rors during the running of a program.
  43. [43]
    [PDF] MODEL CHECKING
    The logic CTL* is a super-set of both CTL and LTL. LTL and CTL coincide if the model has only one path! 24... Page 25. Property Patterns: Motivation.
  44. [44]
    [PDF] Lecture Notes on Computations & Computation Tree Logic
    CTL has the advantage of having a pretty simple model checking algorithm. 2 Kripke Structures. Definition 1 (Kripke structure). A Kripke frame (W,r) consists ...
  45. [45]
    [PDF] Model Checking: Software and Beyond
    May 7, 2007 · [Clarke and Emerson (1981)] Edmund M. Clarke and E. Allen Emerson. Synthesis of Synchro- nization Skeletons for Branching Time Temporal Logic.
  46. [46]
    [PDF] Verification - Rice University
    Model Checking: given a Kripke structure K and an LTL formula ψ, do all computations of K satisfy ψ? In this section we describe the automata-theoretic approach ...
  47. [47]
    [PDF] Partial Model Checking using Networks of Labelled Transition ...
    State explosion can be tackled by divide-and-conquer approaches regrouped under the vocable compositional verification, which take advantage of the com-.<|control11|><|separator|>
  48. [48]
    [PDF] Software model checking - Colorado State University
    BDD-based model checkers, such as SMV [McMillan 1993], have been extremely successful in hardware model checking. They were also used as back-ends for ...
  49. [49]
    [PDF] Model Checking with the Partial Order Reduction
    The partial order reduction is aimed at reducing the size of the state space that needs to be searched. It exploits the commutativity of concurrently ...
  50. [50]
    [PDF] The Birth of Model Checking - CMU School of Computer Science
    Verifying software causes some problems for Model Checking. Software tends to be less structured than hardware. In addition, concurrent software is usually ...
  51. [51]
    [PDF] Counterexample-guided Abstraction Refinement - Stanford University
    Abstract models may admit erroneous (or “spurious”) counterexamples. We devise new symbolic techniques which analyze such counterexamples and refine the ...Missing: challenges | Show results with:challenges
  52. [52]
    Randoop: feedback-directed random testing for Java
    Randoop is a well-known tool that proposes a feedback-directed algorithm for automatic and random generation of unit tests for a given Java class. It ...
  53. [53]
    Software unit test coverage and adequacy | ACM Computing Surveys
    We survey the research work in this area. The notion of adequacy criteria is examined together with its role in software dynamic testing. A review of criteria ...Missing: levels | Show results with:levels
  54. [54]
    The Oracle Problem in Software Testing: A Survey - IEEE Xplore
    Nov 20, 2014 · This paper provides a comprehensive survey of current approaches to the test oracle problem and an analysis of trends in this important area of software ...
  55. [55]
    Model-Based Testing in Practice: An Industrial Case Study using ...
    Model-based testing (MBT) automates software testing, including test generation. This study uses GraphWalker (GW) to compare MBT with manual testing in a TCMS ...
  56. [56]
    Theoretical and empirical studies on using program mutation to test ...
    Theoretical and empirical studies on using program mutation to test the functional correctness of programs ; Timothy A. · Budd · Yale ; Richard A. · DeMillo · Georgia ...
  57. [57]
    A Survey of Runtime Monitoring Instrumentation Techniques - arXiv
    Aug 24, 2017 · In this paper we compare and contrast the various types of monitoring methodologies found in the current literature, and classify them into a ...Missing: analysis | Show results with:analysis
  58. [58]
    A survey of software runtime monitoring - IEEE Xplore
    In this paper, starting from the basic concepts of software runtime monitoring, the basic architecture and monitoring levels of software runtime monitoring are ...Missing: program | Show results with:program
  59. [59]
    Uncertainty in runtime verification: A survey - ScienceDirect
    Runtime Verification can be defined as a collection of formal methods for studying the dynamic evaluation of execution traces against formal specifications.
  60. [60]
    Using AOP for detailed runtime monitoring instrumentation
    In this paper we demonstrate the need for AOP to be extended if it is to support broad runtime monitoring needs, and then present two new joinpoint types for ...
  61. [61]
    The Daikon system for dynamic detection of likely invariants
    Daikon is a system for dynamic detection of likely program invariants. It reports properties true over observed program executions.
  62. [62]
    Gprof: A call graph execution profiler - ACM Digital Library
    Gprof is an execution profiler that accounts for the running time of called routines in the running time of the routines that call them.
  63. [63]
    [PDF] A Survey of Runtime Monitoring Instrumentation Techniques
    Offline monitors are particularly suitable for properties that can only be verified by globally analysing the complete execution trace that is generated once ...
  64. [64]
    A survey of challenges for runtime verification from advanced ...
    Nov 11, 2019 · Since monitoring needs to be carried out over a distributed architecture, this will inherently induce non-deterministic computation.
  65. [65]
    An In-Depth Study of Runtime Verification Overheads during ...
    Sep 11, 2024 · (3) Contrary to conventional wisdom, RV overhead in most projects is dominated by instrumentation, not monitoring. (4) 36.74% of monitoring time ...
  66. [66]
    [PDF] A survey of program slicing techniques - FRANK TIP
    This survey presents an overview of program slicing, including the various general approaches used to compute slices, as well as the specific techniques used ...
  67. [67]
  68. [68]
    Dynamic program slicing - ScienceDirect.com
    A dynamic program slice is an executable subset of the original program that produces the same computations on a subset of selected variables and inputs.
  69. [69]
    The program dependence graph and its use in optimization
    In this paper we present an intermediate program representation, called the program dependence graph (PDG), that makes explicit both the data and control ...
  70. [70]
    A survey of compiler optimization techniques - ACM Digital Library
    This survey describes the major optimization techniques of compilers and groups them into three categories: machine dependent, architecture dependent, and ...
  71. [71]
    A unified approach to global program optimization - Semantic Scholar
    A technique is presented for global analysis of program structure in order to perform compile time optimization of object code generated for expressions ...
  72. [72]
    [PDF] A Catalogue of Optimizing Transformations - Rice University
    Frances. E. Allen,. John. Cocke. We now consider the machine dependent transformations. INSTRUCTION. SCHEDULING. In this optimization, sequences of instructions.
  73. [73]
    [PDF] A Brief History of Just-In-Time - Department of Computer Science
    Software systems have been using “just-in-time” compilation (JIT) techniques since the. 1960s. Broadly, JIT compilation includes any translation performed ...
  74. [74]
    [PDF] The Jalapeño Dynamic Optimizing Compiler for JavaTM
    This paper describes the design of the Jalapeño. Optimizing Compiler and the implementation results that we have obtained thus far. To the best of our knowledge ...
  75. [75]
    [PDF] Machine Learning in Compiler Optimization
    The technique proposed in [17] achieves an average speedup between 1.11x and 1.33x across four GPU architectures and does not lead to degraded performance on a ...
  76. [76]
    [PDF] Optimizing Whole Programs for Code Size - Publish
    I show that my system can improve speed by more than 6% or reduce code size by more than 50% on widely used software packages, depending on the software set and ...
  77. [77]
    Exploring the space of optimization sequences for code-size reduction
    In this paper, we use 15,000 programs from a public collection to explore the optimization space of LLVM, focusing on code-size reduction. This exploration ...
  78. [78]
    Automatic verification of finite-state concurrent systems using ...
    Abstract. We give an efficient procedure for verifying that a finite-state concurrent system meets a specification expressed in a (propositional, branching-time) ...
  79. [79]
    [PDF] Finding Bugs is Easy - Department of Computer Science
    We have implemented a number of automatic bug pattern detectors in a tool called FindBugs. In this paper, we will describe some of the bug patterns our tool ...
  80. [80]
    [PDF] PROGRAM SLICING* Mark Weiser
    The reduced program, called a "slice", is an indepen- dent program guaranteed to faithfully represent the original program within the domain of the specified ...
  81. [81]
    [PDF] Dynamic Taint Analysis for Automatic Detection, Analysis, and ...
    In this paper we propose dynamic taint analysis for au- tomatic detection of overwrite attacks, which include most types of exploits. This approach does not ...
  82. [82]
    [PDF] Escape Analysis for Java
    This paper presents a simple and efficient data flow algorithm for escape analysis of objects in Java programs to determine.
  83. [83]
    Static Code Analysis Using SonarQube : A Step-by-Step Guide ...
    Aug 5, 2024 · In this article, you'll learn how static code analysis works, what it can do for the quality of your codebase, and how to run static code ...
  84. [84]
    CI/CD Pipeline | Safety Critical Continuous Integration Tools - LDRA
    LDRA CI/CD tools support CI/CD pipelines with CV by embedding core verification activities into automated workflows and integrating with leading CI platforms, ...
  85. [85]
    DO-178() Software Standards Documents & Training - RTCA
    DO-178(), originally published in 1981, is the core document for defining both design assurance and product assurance for airborne software.
  86. [86]
    Federal Agency Fulfills Rigorous DO-178C Standard With Unified ...
    Automate C/C++ testing for embedded air navigation software development. Reduce code testing time, cut costs with static analysis, unit testing & more.Missing: program | Show results with:program
  87. [87]
    [PDF] Finding Security Vulnerabilities in Java Applications with Static ...
    This paper proposes a static analysis technique for detecting many recently discovered application vulner- abilities such as SQL injections, cross-site ...
  88. [88]
    [PDF] KLEE: Unassisted and Automatic Generation of High-Coverage ...
    We present a new symbolic execution tool, KLEE, ca- pable of automatically generating tests that achieve high coverage on a diverse set of complex and.
  89. [89]
    [PDF] Control-Flow Integrity Principles, Implementations, and Applications
    This paper describes and studies one mitigation technique, the enforcement of Control-. Flow Integrity (CFI), that aims to meet these standards for ...Missing: seminal | Show results with:seminal
  90. [90]
    OpenText™ Static Application Security Testing (Fortify)
    OpenText Static Application Security Testing (Fortify) helps developers find & fix code vulnerabilities early with automated static code analysis.
  91. [91]
    Valgrind Home
    Valgrind is an instrumentation framework for building dynamic analysis tools. There are Valgrind tools that can automatically detect many memory management and ...Current Releases · The Valgrind Quick Start Guide · Tool Suite · Code Repository
  92. [92]
    Static detection of cross-site scripting vulnerabilities
    This paper presents a static analysis for finding XSS vulnerabilities that directly addresses weak or absent input validation.
  93. [93]
    Advanced Persistent Threat Compromise of Government Agencies ...
    Apr 15, 2021 · The threat actor has been observed leveraging a software supply chain compromise of SolarWinds Orion products[2 ] (see Appendix A). The ...