Samuel Z. Guyer
Back to my web-page

 

CACM

In Defense of Soundiness: A Manifesto

Ben Livshits, Manu Sridharan, Yannis Smaragdakis, Ondrej Lhotak, J. Nelson Amaral, Bor-Yuh Evan Chang, Sam Guyer, Uday Khedker, Anders Moller, and Dimitrios Vardoulakis

Paper: [PDF]

SIGCSE 2014

Metaphors We Teach By

Joseph P. Sanford, Aaron Tietz, Saad Farooq, Samuel Z. Guyer, R. Benjamin Shapiro

Paper: [PDF]

Abstract:

InfoVis 2013

Heapviz: Interactive Heap Visualization for Program Understanding and Debugging

Sean Kelley, Edward Aftandilian, Connor Gramazio, Nathan P. Ricci, Sara L. Su, Samuel Z. Guyer

Paper: [PDF]

Abstract:

ISMM 2013

Elephant Tracks: Portable Production of Complete and Precise GC Traces

Nathan P. Ricci, Samuel Z. Guyer, J. Eliot B. Moss

Paper: [PDF]

Software: Elephant tracks page

Abstract:

We present Elephant Tracks (ET), a dynamic program analysis tool for Java that produces detailed traces of garbage collection-related events, including object allocations, object deaths, and pointer updates. Like prior work, our tracing tool is based on the Merlin algorithm [6,7], but offers several substantial new capabilities. First, it is much more precise than previous tools: it traces method entries and exits and measures time in terms of them, allowing it to place events precisely in the context of the program structure. Second, it is implemented using a combination of JVM Tool Interface (JVMTI)[13] callbacks and bytecode rewriting, and works with any standard JVM. Finally, it produces complete traces, including weak references, events from the Java Native Interface and sun.misc.Unsafe, and VM start up objects. In this paper we also explore the general design space of tracing tools, and carefully define the execution model that the traces represent.

VISSOFT 2013

Visualizing the Allocation and Death of Objects

Raoul L. Veroy, Nathan P. Ricci, Samuel Z. Guyer

Paper: [PDF]

Abstract:

We present memory allocation and death plots, a visualization technique for showing both which method an object is allocated in a Java program, and in which method that object eventually dies. This relates the place in a program’s execution where memory is first used to the place it is no longer used, thus helping the programmer to better understand the memory behavior of a program.

ISMM 2012

new Scala() instance of Java: a comparison of the memory behaviour of Java and Scala programs

Andreas Sewe, Mira Mezini, Aibek Sarimbekov, Danilo Ansaloni, Walter Binder, Nathan P. Ricci, Samuel Z. Guyer

Paper: [PDF]

Abstract:

OOPSLA 2011

Asynchronous Assertions

Edward E. Aftandilian, Samuel Z. Guyer, Martin Vechev, Eran Yahav

Paper: [PDF]

Abstract:

Assertions are a familiar and widely used bug detection technique. Traditional assertion checking, however, is performed synchronously, imposing its full cost on the runtime of the program. As a result, many useful kinds of checks, such as data structure invariants and heap analyses, are impractical because they lead to extreme slowdowns.

We present a solution that decouples assertion evaluation from program execution: assertions are checked <>asynchronously by separate checking threads while the program continues to execute. Our technique guarantees that asynchronous evaluation always produces the same result as synchronous evaluation, even if the program concurrently modifies the program state. The checking threads evaluate each assertion on a consistent snapshot of the program state as it existed at the moment the assertion started.

We implemented our technique in a system called Strobe, which supports asynchronous assertion checking in both single-and multi-threaded Java applications. Strobe runs inside the Java virtual machine and uses copy-on-write to construct snapshots incrementally, on-the-fly. Our system includes all necessary synchronization to support multiple concurrent checking threads, and to prevent data races with the main program threads. We find that asynchronous checking significantly outperforms synchronous checking, incurring tolerable overheads -- in the range of 10% to 50% over no checking at all -- even for heavy-weight assertions that would otherwise result in crushing slowdowns.

IV 2011

Heapviz: Interactive Heap Visualization for Program Understanding and Debugging

Sean Kelley, Edward E. Aftandilian, Connor Gramazio, Nathan Ricci, Sara L. Su, and Samuel Z. Guyer

Paper: [PDF]

Abstract:

Understanding the data structures in a program is crucial to understanding how the program works, or why it doesn't work. Inspecting the code that implements the data structures, however, is an arduous task and often fails to yield insights into the global organization of a program's data. Inspecting the actual contents of the heap solves these problems but presents a significant challenge of its own: finding an effective way to present the enormous number of objects it contains.

In this paper we present Heapviz, a tool for visualizing and exploring snapshots of the heap obtained from a running Java program. Unlike existing tools, such as traditional debuggers, Heapviz presents a global view of the program state as a graph, together with powerful interactive capabilities for navigating it. Our tool employs several key techniques that help manage the scale of the data. First, we reduce the size and complexity of the graph by using algorithms inspired by static shape analysis to aggregate the nodes that make up a data structure. Second, we implement a powerful visualization component whose interactive interface provides extensive support for exploring the graph. The user can search for objects based on type, connectivity, and field values; group objects; and color or hide and show each group. The user may also inspect individual objects to see their field values and neighbors in the graph. These interactive abilities help the user manage the complexity of these huge graphs.

By applying Heapviz to both constructed and real-world examples, we show that Heapviz provides programmers with a powerful and intuitive tool for exploring program behavior.

OOPSLA 2010

What Can the GC Compute Efficiently? A Language for Heap Assertions at GC Time

Christof Reichenbach, Neil Immerman, Yannis Smaragdakis, Eddie Aftandilian, Samuel Z. Guyer.

Paper: [PDF]

Abstract:

We present the DeAL language for heap assertions that are efficiently evaluated during garbage collection time. DeAL is a rich, declarative, logic-based language whose programs are guaranteed to be executable with good whole-heap locality, i.e., within a single traversal over every live object on the heap and a finite neighborhood around each object. As a result, evaluating DeAL programs incurs negligible cost: for simple assertion checking at each garbage collection, the end-to-end execution slowdown is below 2%. DeAL is integrated into Java as a VM extension and we demonstrate its efficiency and expressiveness with several applications and properties from the past literature.

Compared to past systems for heap assertions, DeAL is distinguished by its very attractive expressiveness/efficiency tradeoff: it offers a significantly richer class of assertions than what past systems could check with a single traversal. Conversely, past systems that can express the same (or more) complex assertions as DeAL do so only by suffering orders-of-magnitude higher costs.

SOFTVIS 2010

Heapviz: Interactive Heap Visualization for Program Understanding and Debugging

Eddie Aftandilian, Sean Kelley, Connor Gramazio, Nathan Ricci, Sara Su, and Samuel Z. Guyer

Paper: [PDF]

Abstract:

Understanding the data structures in a program is crucial to understanding how the program works, or why it doesn't work. Inspecting the code that implements the data structures, however, is an arduous task and often fails to yield insights into the global organization of a program's data. Inspecting the actual contents of the heap solves these problems but presents a significant challenge of its own: finding an effective way to present the enormous number of objects it contains.

In this paper we present Heapviz, a tool for visualizing and exploring snapshots of the heap obtained from a running Java program. Unlike existing tools, such as traditional debuggers, Heapviz presents a global view of the program state as a graph, together with powerful interactive capabilities for navigating it. Our tool employs several key techniques that help manage the scale of the data. First, we reduce the size and complexity of the graph by using algorithms inspired by static shape analysis to aggregate the nodes that make up a data structure. Second, we introduce a dominator-based layout scheme that emphasizes hierarchical containment and ownership relations. Finally, the interactive interface allows the user to expand and contract regions of the heap to modulate data structure detail, inspect individual objects and field values, and search for objects based on type or connectivity. By applying Heapviz to both constructed and real-world examples, we show that Heapviz provides programmers with a powerful and intuitive tool for exploring program behavior.

PLDI 2010

Breadcrumbs: Efficient Context Sensitivity for Dynamic Bug Detection Analyses

Michael D. Bond, Graham Z. Baker, and Samuel Z. Guyer

Paper: [PDF]

Abstract:

Calling context---the set of active methods on the stack---is critical for understanding the dynamic behavior of large programs. Dynamic program analysis tools, however, are almost exclusively context insensitive because of the prohibitive cost of representing calling contexts at run time. Deployable dynamic analyses, in particular, have been limited to reporting only static program locations.

This paper presents Breadcrumbs, an efficient technique for recording and reporting dynamic calling contexts. It builds on an existing technique for computing a compact (one word) encoding of each calling context that client analyses can use in place of a program location. The key feature of our system is a search algorithm that can reconstruct a calling context from its encoding using only a static call graph and a small amount of dynamic information collected at cold (infrequently executed) callsites. Breadcrumbs requires no offline training or program modifications, and handles all language features, including dynamic class loading.

We use Breadcrumbs to add context sensitivity to two dynamic analyses: a data-race detector and an analysis for diagnosing null pointer exceptions. On average, it adds 10% to 20% runtime overhead, depending on a tunable parameter that controls how much dynamic information is collected. Collecting less information lowers the overhead, but can result in a search space explosion. In some cases this causes reconstruction to fail, but in most cases Breadcrumbs produces nontrivial calling contexts that have the potential to significantly improve both the precision of the analyses and the quality of the bug reports.

PLDI 09

GC Assertions: Using the Garbage Collector to Check Heap Properties

Edward Aftandilian and Samuel Z. Guyer

Paper: [PDF]

This is an expanded version of our MSPC 2008 paper.

Abstract:

This paper introduces GC assertions, a system interface that programmers can use to check for errors, such as data structure invariant violations, and to diagnose performance problems, such as memory leaks. GC assertions are checked by the garbage collector, which is in a unique position to gather information and answer questions about the lifetime and connectivity of objects in the heap. By piggybacking on existing garbage collector computations, our system is able to check heap properties with very low overhead -- around 3% of total execution time -- low enough for use in a deployed setting.

We introduce several kinds of GC assertions and describe how they are implemented in the collector. We also describe our reporting mechanism, which provides a complete path through the heap to the offending objects. We report results on both the performance of our system and the experience of using our assertions to find and repair errors in real-world programs.

CACM 2008

Wake Up and Smell the Coffee: Evaluation Methodology for the 21st Century

Blackburn et al.

Paper: [PDF]

Abstract:

Evaluation methodology underpins all innovation in experimental computer science. It requires relevant workloads, appropriate experimental design, and rigorous analysis. Unfortunately, methodology is not keeping pace with the changes in our field. The rise of managed languages such as Java, C#, and Ruby in the past decade and the imminent rise of commodity multicore architectures for the next de- cade pose new methodological challenges that are not yet widely understood. This paper explores the consequences of our collective inattention to methodology on innovation, makes recommendations for addressing this problem in one domain, and provides guidelines for other domains. We describe benchmark suite design, experimental design, and analysis for evaluating Java applications. For example, we introduce new criteria for measuring and selecting di- verse applications for a benchmark suite. We show that the complexity and nondeterminism of the Java runtime system make experimental design a first-order consideration, and we recommend mechanisms for addressing complexity and nondeterminism. Drawing on these results, we suggest how to adapt methodology more broadly. To continue to deliver innovations, our field needs to significantly increase partici- pation in and funding for developing sound methodological foundations.

OOPSLA 07

Tracking Bad Apples: Reporting the Origin of Null and Undefined Value Errors

Michael D. Bond, Nicholas Nethercote, Stephen W. Kent, Samuel Z. Guyer, and Kathryn S. McKinley

Paper: [PDF]

Abstract:

Programs sometimes crash due to unusable values, for example, when Java and C# programs dereference null pointers and when C and C++ programs use undefined values to affect program behavior. A stack trace produced on such a crash identifies the effect of the unusable value, not its cause, and is often not much help to the programmer.

This paper presents efficient origin tracking of unusable values; it shows how to record where these values come into existence, correctly propagate them, and report them if they cause an error. The key idea is value piggybacking: when the original program stores an unusable value, value piggybacking instead stores origin information in the spare bits of the unusable value. Modest compiler support alters the program to propagate these modified values through operations such as assignments and comparisons. We evaluate two implementations: the first tracks null pointer origins in a JVM, and the second tracks undefined value origins in a memory-checking tool built with Valgrind. These implementations show that origin tracking via value piggybacking is fast and often useful, and in the Java case, has low enough overhead for use in a production environment.

TFP 07

Bytecode Verification for Haskell

Rob Dockins and Samuel Z. Guyer

Paper: [PDF]

Abstract:

In this paper we present a method for verifying Yhc bytecode, an intermediate form of Haskell suitable for mobile code applications. We examine the issues involved with verifying Yhc bytecode programs, and we present a proof-of-concept bytecode compiler and verifier.

Verification is a static analysis which ensures that a bytecode program is type-safe. The ability to check type-safety is important for mobile code situations where un- trusted code may be executed. Type-safety subsumes the critical memory-safety property and excludes important classes of exploitable bugs, such as buffer over- flows. Haskell's rich type system also allows programmers to build effective, static security policies and enforce them using the type checker. Verification allows us to be confident the constraints of our security policy are enforced.

OOPSLA 06

The DaCapo Benchmarks: Java Benchmarking Development and Analysis

with Steve Blackburn et al.

Paper: [PDF]

Abstract:

Since benchmarks drive computer science research and industry product development, which ones we use and how we evaluate them are key questions for the community. Despite complex runtime tradeoffs due to dynamic compilation and garbage collection required for Java programs, many evaluations still use methodologies developed for C, C++, and Fortran. SPEC, the dominant purveyor of benchmarks, compounded this problem by institutionalizing these methodologies for their Java benchmark suite. This paper recommends benchmarking selection and evaluation methodologies, and introduces the DaCapo benchmarks, a set of open source, client-side Java benchmarks. We demonstrate that the complex interactions of (1) architecture, (2) compiler, (3) virtual machine, (4) memory management, and (5) application require more extensive evaluation than C, C++, and Fortran which stress (4) much less, and do not require (3). We use and introduce new value, time-series, and statistical metrics for static and dynamic properties such as code complexity, code size, heap composition, and pointer mutations. No benchmark suite is definitive, but these metrics show that DaCapo improves over SPEC Java in a variety of ways, including more complex code, richer object behaviors, and more demanding memory system requirements. This paper takes a step towards improving methodologies for choosing and evaluating benchmarks to foster innovation in system design and implementation for Java and other managed languages.

PLDI 06

Free-Me: A Static Analysis for Automatic Individual Object Reclamation

Samuel Z. Guyer, Kathryn S. McKinley and Daniel Frampton

Also see complete results graphs for mark/sweep and GenMS collectors.

Paper: [PDF]

Abstract:

Garbage collection has proven benefits, including fewer memory-related errors and reduced programmer effort. Garbage collection, however, trades space for time. It reclaims memory only when it is invoked: invoking it more frequently reclaims memory quickly, but incurs a significant cost; invoking it less frequently fills memory with dead objects. In contrast, explicit memory management provides prompt low cost reclamation, but at the expense of programmer effort.

This work comes closer to the best of both worlds by adding novel compiler and runtime support for compiler inserted frees to a garbage-collected system. The compiler's free-me analysis identifies when objects become unreachable and inserts calls to free. It combines a lightweight pointer analysis with liveness information that detects when short-lived objects die. Our approach differs from stack and region allocation in two crucial ways. First, it frees objects incrementally exactly when they become unreachable, instead of based on program scope. Second, our system does not require allocation-site lifetime homogeneity, and thus frees objects on some paths and not on others. It also handles common patterns: it can free objects in loops and objects created by factory methods.

We evaluate free() variations for free-list and bump-pointer allocators. Explicit freeing improves performance by promptly reclaiming objects and reducing collection load. Compared to mark-sweep alone, free-me cuts total time by 22% on average, collector time by 50% to 70%, and allows programs to run in 17% less memory. This combination retains the software engineering benefits of garbage collection while increasing space efficiency and improving performance, and thus is especially appealing for real-time and space constrained systems.

CC 06

Efficient Flow-Sensitive Interprocedural Data-flow Analysis in the Presence of Pointers

Teck Bok Tok, Samuel Z. Guyer and Calvin Lin

Paper: [PDF]

Abstract:

This paper presents a new worklist algorithm that significantly speeds up a large class of flow-sensitive data-flow analyses, including typestate error checking and pointer analysis. Our algorithm works particularly well for inter- procedural analyses. By contrast, traditional algorithms work well for individual procedures but do not scale well to interprocedural analysis because they spend too much time unnecessarily re-analyzing large parts of the program. Our algo- rithm solves this problem by exploiting the sparse nature of many analyses. The key to our approach is the use of interprocedural def-use chains, which allows our algorithm to re-analyze only those parts of the program that are affected by changes in the flow values. Unlike other techniques for sparse analysis, our algo- rithm does not rely on precomputed def-use chains, since this computation can itself require costly analysis, particularly in the presence of pointers. Instead, we compute def-use chains on the fly during the analysis, along with precise pointer information. When applied to large programs such as nn, our techniques im- prove analysis time by up to 90% -- from 1974s to 190s -- over a state of the art algorithm.

SCP 05

Error Checking with Client-Driven Pointer Analysis

Samuel Z. Guyer and Calvin Lin

Science of Computer Programming, Special Issue on the Static Analysis Symposium 2003

Paper: [Postscript] [PDF]

Abstract:

This paper presents a new client-driven pointer analysis algorithm that automatically adjusts its precision in response to the needs of client analyses. Using five significant error detection problems as clients, we evaluate our algorithm on 18 real C programs. We compare the accuracy and performance of our algorithm against several commonly-used fixed-precision algorithms. We find that the client-driven approach effectively balances cost and precision, often producing results as accurate as fixed-precision algorithms that are many times more costly. Our algorithm works because many client problems only need a small amount of extra precision applied to selected portions of each input program.

OOPSLA 04

Finding Your Cronies: Static Analysis for Dynamic Object Colocation

Samuel Z. Guyer and Kathryn S. McKinley

Paper: [PDF][Slides]

Abstract:

This paper introduces dynamic object colocation, an optimization to reduce copying costs in generational and other incremental garbage collectors by allocating connected objects together in the same space. Previous work indicates that connected objects belong together because they often have similar lifetimes. Generational collectors, however, allocate all new objects in a nursery space. If these objects are connected to data structures residing in the mature space, the collector must copy them. Our solution is a cooperative optimization that exploits compiler analysis to make runtime allocation decisions. The compiler analysis discovers potential object connectivity for newly allocated objects. It then replaces these allocations with calls to coalloc, which takes an extra parameter called the colocator object. At runtime, coalloc() determines the location of the colocator and allocates the new object together with it in either the nursery or mature space. Unlike pretenuring, colocation makes precise per-object allocation decisions and does not require lifetime analysis or allocation site homogeneity. Experimental results for SPEC Java benchmarks using JikesRVM show colocation can reduce garbage collection time by 50% to 75%, and total performance by up to 10%.

IEEE 04

Broadway: A Compiler for Exploiting the Domain-Specific Semantics of Software Libraries

Samuel Z. Guyer Calvin Lin

Proceedings of the IEEE: Special Issue on Program Generation, Optimization, and Platform Adaptation

Paper: [Postscript] [PDF]

Abstract:

This paper describes the Broadway compiler and our experiences using it to support domain-specific compiler optimizations. Our goal is to provide compiler support for a wide range of domains and to do so in the context of existing programming languages. Therefore we focus on a technique that we call library-level optimization, which recognizes and exploits the domain-specific semantics of software libraries. The key to our system is a separation of concerns: compiler expertise is built into the Broadway compiler machinery, while domain expertise resides in separate annotation files that are provided by domain experts. We describe how this system can optimize parallel linear algebra codes written using the PLAPACK library. We find that our annotations effectively capture PLAPACK expertise at several levels of abstraction, and that our compiler can automatically apply this expertise to produce considerable performance improvements. Our approach shows that the abstraction and modularity found in modern software can be as much as asset to the compiler as it is to the programmer.

SAS 03

Client-Driven Pointer Analysis

Samuel Z. Guyer and Calvin Lin

June 2003 with FCRC '03.

Paper: [Postscript] [PDF] [Slides]

Abstract:

This paper presents a new client-driven pointer analysis algorithm that automatically adjusts its precision in response to the needs of client analyses. We evaluate our algorithm on 18 real C programs, using five significant error detection problems as clients. We compare the accuracy and performance of our algorithm against several commonly-used fixed-precision algorithms. We find that the client-driven approach effectively balances cost and precision, often producing results as accurate as fixed-precision algorithms that are many times more costly. Our algorithm works because many client problems only need a small amount of extra precision applied to the right places in each input program.

Dissertation

Incorporating Domain-Specific Information into the Compilation Process

Advisor: Calvin Lin

May 2003

Paper: [Postscript] [PDF] [Slides]

Abstract:

Despite many advances in compiler research, traditional compilers continue to suffer from one significant limitation: they only recognize the low-level primitive constructs of their languages. In contrast, programmers increasingly benefit from higher level software components, which implement a variety of specialized domains---everything from basic file access to 3D graphics and parallel programming. The result is a marked difference between the level of abstraction in software development and the level of abstraction in compilation.

In this thesis we present the Broadway compiler, which closes this gap. Broadway represents a new kind of compiler, called a library-level compiler, that supports domain-specific compilation by extending the benefits of compiler support to software libraries. The key to our approach is a separate annotation language that conveys domain-specific information about libraries to our compiler, allowing it to treat library routines more like built-in language operations. Using this information, the compiler can perform library-level program analysis and apply library-level optimizations.

We explore both the opportunities and challenges presented by library-level compilation. We show that library-level optimizations can increase the performance of several parallel programs written using a highly-tuned parallel linear algebra library. These high-level optimizations are beyond the capabilities of a traditional compiler and even rival the performance of programs hand-coded by an expert. We also show that our compiler is an effective tool for detecting a range of library-level errors, including several significant security vulnerabilities. Finally, we present a new client-driven pointer analysis algorithm, which provides precise and scalable program analysis to meet the demanding requirements of library-level compilation.

UTCS TR-02-04

Detecting Errors with Configurable Whole-Program Dataflow Analysis

Samuel Z. Guyer, Emery D. Berger and Calvin Lin

Paper: [PDF]

Abstract:

Broadway: A Software Architecture for Scientific Computing

Samuel Z. Guyer and Calvin Lin

R. F. Boisvert and P. T. P. Tang Editors. Kluwer Academic Press, 2000.

The Architecture of Scientific Software.

Paper: [Postscript] [PDF]

Abstract:

LCPC 00

Optimizing the Use of High Performance Libraries

Samuel Z. Guyer and Calvin Lin

Paper: [Postscript] [PDF] [Slides]

Abstract:

This paper describes how the use of software libraries, which is prevalent in high performance computing, can benefit from compiler optimizations in much the same way that conventional programming languages do. We explain how the compilation of these informal languages differs from the compilation of more conventional languages. In particular, such compilation requires precise pointer analysis, domain-specific information about the library's semantics, and a configurable compilation scheme. We describe a solution that combines dataflow analysis and pattern matching to perform configurable optimizations.

DSL 99

An Annotation Language for Optimizing Software Libraries

Samuel Z. Guyer and Calvin Lin

October 1999.

Paper: [Postscript] [PDF]

Abstract:

This paper introduces an annotation language and a compiler that together can customize a library implementation for specific application needs. Our approach is distinguished by its ability to exploit high level, domain-specific information in the customization process. In particular, the annotations provide semantic information that enables our compiler to analyze and optimize library operations as if they were primitives of a domain-specific language. Thus, our approach yields many of the performance benefits of domain-specific languages, without the effort of developing a new compiler for each domain. This paper presents the annotation language, describes its role in optimization, and illustrates the benefits of the overall approach. Using a partially implemented compiler, we show how our system can significantly improve the performance of two applications written using the PLAPACK parallel linear algebra library.