Vizmake: Debugging Makefiles via Visualization Wenbin Fang Daniel Crowell wenbin@cs.wisc.edu danieljc@cs.wisc.edu Abstract Build tools (e.g., GNU “make”) automate the tedious work of repeatedly invoking compilers in the correct order with the right flags and input files by using developer-provided build description files (e.g., Makefiles). However, build description files can easily become very complex and buggy, as a project evolves. There is no good built-in debugging support for build description files. Developers tend to resort to manual inspection of the build description file. In this project, we conducted a comprehensive Makefile bug study on open source projects that use GNU “make” and discovered that the two common root causes for most Makefile bugs are using variables with incorrect values and specifying incorrect dependencies in Makefile rules (i.e., missing dependencies or extra dependencies). We developed a tool, Vizmake, to automatically visualize variable (e.g., variable value, source of definition ...) and dependency information (e.g., potential dependency problems for certain targets) during build time, which provides a good starting point for developers to interactively debug problems related to incorrect variables and dependencies in Makefiles. Vizmake leverages an instrumented GNU “make” and an unmodified strace to collect trace data during build time, and then it analyzes the trace data and generates web pages that visualize variable and dependency information. We conducted a case study on using Vizmake to debug real problems of open source projects including GCC, GDB and Apache Httpd, which demonstrates the ability of vizmake to allow developers to visually identify the root causes of bugs. 1 Introduction The build tool plays an important role in the software development cycle, where it automates the tedious work of repeatedly invoking compilers in the correct order with the right flags and input files by using developer-provided build scripts. Make [4] is such a build tool that uses description files, or Makefiles, to produce the desired artifacts. Developers write rules in Makefiles to explicitly capture “dependencies” between “build targets” (e.g., executables, object files ...), and to specify “executable commands” to produce 1 “build targets”. Incremental compilation is supported via a timestamp-based heuristic that ensures that a rule’s target is only rebuilt if at least one of its dependees is newer. The following text shows an example rule in a Makefile, where “prog” is a build target that depends on three files “x.o”, “y.o”, and “z.o”. To produce “prog”, the command in the second line should be executed. prog : x . o y . o z . o c c x . o y . o z . o −l S −o prog However, the build description file can easily become very complex and buggy. The complexity of the build description file grows as the project evolves. For example, when adding new source files, deleting outdated source files, and supporting new platforms in a project, the build description file needs to be updated accordingly. Build description files tend to be buggy, because they are not as well tested as source code files that typically have well-established testing frameworks, and there are no good built-in debugging tools for build description files. Developers tend to resort to manual inspections of build description files, which use declarative-like language and statements that do not execute in linear order. This project is to build tools to help developers debug build description files. Although many alternatives for “make” and GNU “make” have been proposed, none of them have been able to overthrow the original. Therefore, we focus on the GNU “make” program and its description files (or Makefiles) in this project. We first conducted an in-depth study in bug databases and commit logs of open source projects (i.e., MySQL, Firefox, GCC and GDB), trying to understand characteristics and root causes of Makefile bugs. We found that there are primarily two root causes for most bugs in Makefiles, incorrect variable assignments and incorrect dependencies. First, incorrect variable assignments in Makefiles leads to abnormal behaviors at build time. Developers often have a misconception of variable precedence in Makefiles, given the complex variable assignment approaches (e.g., macro definition via command line overwrites variable definition in Makefiles). Second, an incorrect dependency results in either 1) adding unnecessary dependencies that cause unnecessary rebuilding or 2) missing necessary dependencies that break parallel building. The common practice of debugging Makefiles is to manually inspect contents in Makefiles or trace data from running the make program (i.e., make -pn). However, both the contents of a makefile and trace data from running make can be voluminous and not well-structured and in these cases a manual inspection is inefficient and ineffective. However, manual inspection of a Makefile is still indispensable for debugging, because a Makefile is produced by human developers or generated from templates written by human developers, which means there are tons of idioms that are hard to correctly verify through automated tools. As applied in many subareas of software engineering [1], we believe that visualization is also able to guide the process of manual Makefile debugging and help developers better understand Makefiles. Therefore, we designed and 2 implemented vizmake, a tool that automatically visualizes a Makefile’s build-time variable and dependency information, which can be used to effectively guide the Makefile debugging process. In vizmake, build-time variable information includes values of variables and the relationships between variables (i.e., whether a variable uses another). This helps developers identify variable bugs visually, which is also suggested in some literature, e.g., “displaying values could give developers the ability to identify suspicious values” [8]. Build-time dependency information is used to check if the claimed dependencies in a Makefile’s rules are consistent with the actual files accessed when executing those rules. If a dependency is not in a certain rule but is accessed in the commands (e.g., running compiler) in that rule, then this may mean that there is a missing dependency that should be added. On the other hand, if a dependency is in a rule but is not accessed in the commands for that rule, then this may mean that there is an extra dependency that should be removed. In our implementation, vizmake automatically analyzes trace data from running a modified make program and running the Linux system call tracing tool strace. Then, vizmake visualizes variable information and dependency information in well organized web pages. Finally, we evaluated vizmake by conducting a case study of using our tools to diagnose real bugs in Makefiles of real open source software. The result shows that our tools are both efficient and effective. This report is organized as follows. Section 2 surveys related topics on build systems. Section 3 studies bug databases and commit logs of open source software. Section 4 describes the design and implementation of vizmake. The case study on using vizmake to find Makefile bugs is in Section 5. We conclude in Section 6. 2 Related Work In this section, we survey Makefiles debugging and build systems in general. Tamrawi et al. [9] developed SYMake to help developers detect code smells and errors in makefiles. SYMake does static analysis on makefiles and visualizes symbolic dependency graphs for targets, files, and variables. The key departure points in our vizmake in comparison to SYMake is that we analyze and visualize trace data collected during build time, which faithfully reflect the actual values of variables in a makefile at build time. Adams [2] developed MAKAO to understand the build system. MAKAO reads the trace output from “make -p”, and it visualizes the dependency graph for targets and files. By contrast, our vizmake utilizes the trace data from our modified version of the “make” program, which provides more accurate data that fit our needs. Moreover, our vizmake visualizes variables in Makefiles, which enables finding variable bugs. Bernstein [3] developed Remake which can be used to debug Makefiles. Remake helps with identifying the 3 specific place of build errors by adding line number information on outputs as well as a chain of commands in the makefile that led to an error. GNU “make” has simple error checking for semantic errors, but its output is usually limited or not helpful. Another way Remake assists in debugging is by expanding variables and other data in a Makefile to make understanding it easier. Unlike Remake, we will directly present visual representations as well as potential errors in the dependences of a Makefile rather than providing tools for others to manually find this information. Johnson et al. [5] looked at ways to improve the build process. They used Makepp 1 that provided more accurate builds and dependency info compared to a similar build process with GNU “make”. This was especially beneficial for making rebuilding more reliable with fewer portions retouched with the same input rules. The visualization provided by our vizmake can also help developers understand the build process, thus achieving a similar goal as Johnson et al. Jorgensen [6] provides a theoretical foundation for verifying that Makefiles are safe and produce the same output when partially rebuilding a project compared to building it from scratch. This includes ways to check for soundness and completeness of dependencies. In the future, we can consider incorporating their work into vizmake. 3 Bugs Study As a feasibility study for developing debugging tools for Makefiles, we conducted a study on four open source projects, including Apache httpd, GCC, Firefox, and GDB, which are real projects with a wide sample of bugs covering many years of changes and commits. From these projects we analyzed their bug databases and commit logs for Makefiles to identify a variety of bugs that have been encountered and the corresponding root causes. This gives us a better idea of what are widely encountered Makefile bugs along with some examples with which to test the tools we developed. Through the study, we discovered that there are two main types of causes for Makefile bugs. The first type is incorrect variables where a wrong value or no value is assigned, leading to abnormal behaviors at build time. The second type is incorrect dependencies that are either 1) adding unnecessary dependencies that cause unnecessary rebuilding or 2) missing necessary dependencies such as those that show up in parallel building. Table 1 shows a sample of Makefile bugs that were found in this study as well as the root causes of them. Five bugs are due to incorrect variables and three are dependency bugs. The problem with undefined or invalid variables was seen repeatedly in our study, which lead to abnormal behavior of the build process (e.g., a generated file is not deleted via “make clean” in Bug #26552). Finding 1 http://makepp.sourceforge.net/ 4 Project Apache Apache Apache GCC GCC GCC Firefox GDB GDB Bug A generated file is not deleted by “make clean” Copy files from a wrong path Abnormal build behavior Rebuild the CNI header files multiple times with with -j builds. Generating invalid man page Does not recompile source files that include io.h Abnormal clean behavior Refactoring request Source Bug Database #26552 Bug Database #7790 Commit Log on Jan 9 2004 Bug Database #29278 Bug Database #256608 Bug Database #27107 Commit Log on March 14 2008 Bug Database #10649 Unexpected DESTDIR variable Bug Database #7786 Root Cause Variable CLEAN TARGETS doesn’t include that file a NULL $(top blddir) variable Redundant PHONY TARGETS variable definition Incorrect dependency Missing dependency on gccvers.texi Make dependency on io/io.h broken Missing dependencies on clean Undefined variable GDB DATADIR PATH DESTDIR defined via command line argument is not used Table 1: Sample Makefile Bugs and Root Causes Project GCC Firefox Bug Dead targets Abnormal build behavior Bug Source Commit Log July 18 2004 Bug Database #255872 Firefox Unused directory created Commit Log on on Sept 17 2009 Root Cause Targets never be used Typo on a path (not in variable) Extra remove command usage Table 2: Other Makefile Bugs variable-related bugs requires the knowledge of developers, because variables contain semantics and only developers know what values are correct at a given scenario. We developed vizmake to visualize variable values and the relationships between variables, which helps developers manually and visually inspect the value of variables and locate erroneous variables. Examples we found where developers corrected dependency bugs always involved adding missing dependencies, or removing unnecessary dependencies. A common case where dependency-related bugs are encountered is when building a project in parallel where alternate ways of stepping through the commands can uncover faulty dependencies. To inspect dependency-related bugs, developers can start with automatic approaches by following simple rules, e.g., if main.o relies on foo.c, then foo.c must be used to build main.o, and then verify the rule violations. Therefore, we developed vizmake to automatically detect potential incorrect dependencies in Makefiles. In addition to the two major types of root causes for Makefile bugs listed in Table 1, three other types were found, which are shown in Table 2. The first type is a dead target in a Makefile that is never used, where it has nothing to do with dependencies, so it does not belong to dependency bugs. The second type is a simple typo in a dependency list of a rule in Makefile, where this typo is not a value assigned to variables, so it does not belong to the variable-related bugs. The last type is the creation of a blank directory in a make file that was never used, 5 due to an extra directory creation command in the rule. Building tools to inspect them would be great future work. 4 VizMake This section first describes an overview of vizmake, and then we present the design and implementation details. 4.1 Overview Through the bug study in Section 3, we know there are two major root causes for Makefile bugs, namely, incorrect variables and incorrect dependencies. Therefore, we design vizmake to make finding these two types of root causes easy. Vizmake is aimed to automatically produce visualization that faithfully displays Makefile variable and dependency information to developers, allowing developers to effectively and efficiently identify abnormal variables and dependencies. To achieve this goal, vizmake collects trace data during build time, using a modified GNU “make” (denoted as make’ ) and unmodified strace. The visualization is automatically generated right after the build process is complete via make’. Figure 1 summarizes the major workflow of vizmake. Data Collection Run make` and strace Visualization Trace Data Visualization Engine Developer Figure 1: Workflow of vizmake. We implemented Vizmake in Python. Vizmake launches make’ and strace to collect trace data, and then it generates web pages to display visualization. The debugging process is highly interactive, so our visualization allows developers to interactively manipulate the visualization inside a browser. Developers run vizmake in the same way they run GNU “make”. They can pass any GNU “make” command line arguments to vizmake. Vizmake performs normal build process of GNU “make” with trace data generated to /tmp directory. At the end, trace data is analyzed and visualized by generating web pages with D3 [7] javascript code. Vizmake starts a simple web server to host the visualization web pages, and developers use 6 a web browser to view and interact with the visualizations. Figure 2 shows an example index page for the generated visualization. The index page mainly visualizes the hierarchy of processes involved in the build process, so developers can have a basic idea of how the build process works. A “make” process is also associated with three links for showing the complete command line used for this process (CMD), the variable visualization (VAR), and the dependency visualization (DEP). Figure 2: Workflow of vizmake. In the following two subsections, we will describe the details about visualizing variables and dependencies, respectively. 4.2 Visualizing Variables Makefile variables are complex in how they are defined. There are two ways for a variable to be expanded. The first way is to recursively expand a variable that is defined by lines using “=” or by the “define” directive. Such a variable recursively expands all referenced variables only when itself is referenced, that is, the value expansion is on demand. The second way is to simply expand a variable that is defined by lines using “:=”. The value of a simply expanded variable is scanned once, expanded immediately, and used for all references. It is further complicated when a variable is defined outside a Makefile (e.g., environment variable or command line arguments) or is overrode by other definitions (e.g., variable definition from command line takes higher precedence than from Makefile). Makefile-writers’ misconceptions about variable definitions are typically the source of these bugs. The result of our study on Makefile bugs in open source projects in Section 3 guided us to design vizmake in a way that provides enough variable information to facilitate developers in visually identifying abnormal variables. Specifically, vizmake should help developers answer the following questions: 7 • What is the value of a particular variable being used in a particular place? In common programming practice, we oftentimes use a debugger only to inspect the value of some variables. Similarly, faithfully displaying variable values at build time is highly desirable. • Where is a referenced variable defined? This helps developers determine whether or not the variable definition comes from the source they expect. • For a variable, how does its value expand? In other words, what other variables are referenced in a variable’s definition. 4.2.1 Data Collection Similar projects that visualize Makefiles use the trace data either from running GNU “make” (e.g., make -p) [2] or from statically analyzing the Makefile itself [9]. However, trace data from running “make p” or statically analyzing the Makefile is incomplete and does not expand values for recursively expanded variables (i.e., variables assigned by “=”). In addition, neither approach is able to determine the source of a variable definition (i.e., from a Makefile, command line or environment variable). To faithfully reflect Makefile variables at build time, we decided to modify the GNU make program to meet our needs, which now outputs trace data of various variable information such as when a variable is referenced, including variable name, variable value, what other variables are referenced by a variable, which line in a Makefile references what variables, and where the referenced variable is defined (e.g., at which line in which Makefile, or from command line). 4.2.2 Visualization We use an indented tree as the major visualization style in vizmake, because indented trees are widely-used to represent hierarchical structures like file systems, and what vizmake shows is inherently hierarchical too: a Makefile contains lines of texts; a line in the Makefile references some variables; a variable may reference other variables too. Although indented trees require much vertical space, they do allow efficient interactive exploration of the tree to find a specific node. In addition, the indented layout allows rapid scanning of node labels, making bug finding easy. Figure 3 shows the visualization for a Makefile involved when building an open source project lighttpd. The root node (in blue) in the indented tree displays the full path of a Makefile in its label. There are two child nodes (in white) of the root node, where the subtrees rooted at them represent information of included files and lines of text that reference variables, respectively. The orange node represents a line of text, where the label displays the line number and the text. The children of the “line” node represent variables referenced 8 Figure 3: Visualizing Makefile variables. at the current line, whose label shows the name of the referenced variable, the corresponding value, and the source of definition for this variable. If a referenced variable can be simply expanded, then its value is shown in the node’s label directly; otherwise, its value is shown inside a yellow floating tooltip, and all referenced variables are child nodes, if there is any. A red node represents a variable whose value is not from within Makefile, which means a variable is either undefined or defined outside the Makefile (e.g., from command line). We highlight such a node (in red) because if a variable has a value not from the Makefile, then it may not be anticipated by Makefile-writers, which may possibly be a bug. In the same way that debug points are mainly used to inspect variable values , the visualization in vizmake provides developers an effective way to visually inspect Makefile variables. 4.3 Visualizing Dependencies Vizmake is also aimed to provide suggestions to developers about possible dependency problems in the Makefile. Specifically, vizmake holds an invariant: given a rule, the set of files accessed in the command processes should be the same as the set of files the target depends on. Vizmake should report extra dependencies, missing dependencies and other related information to developers, who have the final word on the correctness of dependencies. 9 4.3.1 Data Collection To report potential dependency problem, vizmake requires two sets of files, files that are actually read in the commands to build that target (“accessed files”) and files that are declared as prerequisites for building a target (“dependencies”). Vizmake uses strace to trace system call information for running “make” and for all processes created to build targets, in order to get data about “accessed files”. Specifically, we monitor the open system call to know all files accessed. We also monitor process creation system calls (i.e., fork, vfork, and clone to get all child PIDs, which are used in a later analysis stage to group all accessed files for the same rule. Vizmake uses make’ to collect data about “dependencies”. The make’ also outputs data of child processes that are created to build a particular target, which is used to match the data collected by strace. 4.3.2 Visualization Dependency problems include missing dependencies and extra dependencies. Let the set of “dependencies” be D and the set of “accessed files” be A, then the set of missing dependencies M = A \ D (the set difference between A and D), the set of extra dependencies E = D \ A (the set difference between D and A), and the set of correct dependencies C = D ∩ A (the set intersection of D and A). The set of “dependencies” can be obtained accurately from running make’. However, it is challenging to define the set of “accessed files”. It is tempting to simply define “accessed files” as all files ever successfully opened in child processes of the make process. However, this definition would yield many noises that lead to false missing dependencies or false extra dependencies. After our careful investigation, we list all noises of “assessed files” and the corresponding alleviations to make the “accessed files” definition accurate in practice. • Intermediate files. A rule may generate intermediate files, which would be accessed but not be part of the dependency list in that rule. If we count these files as “assessed files”, then we would get false missing dependencies. Therefore, vizmake excludes those files that are ever opened as write-only files from the set of “assessed files”. • Standard libraries and header files. Standard libraries, header files and other files in standard linux directories are typically read-only and are not specified as dependencies in a Makefile rule. If we count them as “assessed files”, then we would get false missing dependencies. Therefore, vizmake excludes files with prefixes like “/lib”, “/usr/lib”, “/proc” etc. • Directories. Some commands may access directories using the open system call. Typically, directories are not specified as dependencies in a rule. If we count these directories as “assessed files”, then we 10 would get false missing dependencies. Thus, vizmake excludes “files” ending with “/”, “.” and “..”. • Other phony targets as dependencies. A rule may have other phony targets as dependencies. If we count them as “assessed files”, then we would get false extra dependencies, because they are not actual files and are not accessed. There is not any good way to distinguish non-existing files from phony targets, so we resort to letting developers manually discriminate this case. • Empty dependencies of phony targets. A rule may have no dependency for a phony target. For example, it’s very common to have an “all” target with no any dependency. We would always get false missing dependencies for such rules. There is not a good way to distinguish non-existing files from phony targets, so we resort to letting developers manually discriminate this case. • Executable files. Executable files that are executed in commands are tricky to handle. Oftentimes, they are not accessed by the open system call, so we do not count them as “assessed files”, which may lead us to get false extra dependencies. Therefore, we also need to use strace to record system calls like exec. However, if we count them as “assessed files”, then we may get false extra dependencies, since the executable dependencies may not be specified in Makefiles but rather in configuration scripts. To alleviate the problem, we count executable files as “assessed files” only when false extra dependencies occur. Although false negatives are significantly reduced after applying these heuristics, they cannot be totally eliminated. However, the result from running vizmake can be used as a good starting point for manual sophisticated analysis. Vizmake provides as much information as possible for developers. For example, we provide separate CMD pages to show what commands are executed to generate certain targets, and what arguments are passed to the commands. Figure 4 shows the visualized dependency analysis result. An orange node represents a rule that generates a target, whose child nodes are the dependency analysis result. A red node is a missing dependency, a yellow node is an extra dependency, and a green node is a correct dependency. 5 Case Study In this section, we conducted a case study of evaluating our tools for debugging. In the evauation, we wanted to answer this main question: can vizmake help find root causes of real bugs in real open source projects? We used real bugs from GCC, GDB and Apache Httpd for our evaluation, which are summarized in Table 3. 11 Figure 4: Visualizing Makefile Dependencies. 5.1 GDB VAR1 This “bug” is in fact reported as a refactoring request to replace an undefined variable GDB DATADIR PATH. Vizmake can be used to help verify this refactoring request, inspecting whether or not GDB DATADIR PATH references are undefined. First, we looked for the references of GDB DATADIR PATH in all Makefiles involved, because we wanted to know where this variable is referenced by which other variables or in which lines in a Makefile. Therefore, we can have a better idea on how this variable is used in the Makefile at build time. Vizmake provides an ID GDB VAR1 Project GDB 6.8 Bug Description Bug 10649: refactoring request GDB VAR2 GDB 5.2 Bug 7786: DESTDIR variable from command line is not used APACHE VAR1 Apache Httpd 2.0.48 GCC DEP1 GCC 4.2.0 Bug 26552: The file server/export files is generated during the build process, but neither a “make clean” nor a “make distclean” will remove it. Bug 27107: don’t recompile source files for libfortran component that include io/io.h when io/io.h is modified Table 3: Bugs Used in Evaluation 12 Root Cause GDB DATADIR PATH is used, but never defined DESTDIR value is not passed from command line argument to child make process Wrong value in CLEAN TARGETS variable Missing dependency on io/io.h intuitive indented tree structure to present variable reference information. Second, we verified whether or not GDB DATADIR PATH is undefined. Vizmake highlights those variables that are not defined from Makefiles. Therefore, it is easy to identify that all GDB DATADIR PATH references are undefined. Figure 5: Screenshot for GDB VAR1, identifying undefined GDB DATADIR PATH reference. Figure 5 shows a screenshot for an undefined GDB DATADIR PATH reference. This test case suggests a good use of vizmake in refactoring Makefile, which is often reported in bug databases. 5.2 GDB VAR2 This bug is that when invoking “make DESTDIR=/some/dir install”, the value of “/some/dir” is not assigned to the variable DESTDIR when it is referenced. The variable assignment precedence of command line argument is higher than the assignment in Makefiles, so the correct result should be that DESTDIR is assigned to “/some/dir”. To debug this problem using vizmake, we performed these steps: First, we looked for the references to DESTDIR and checked the value of DESTDIR and the source of its definition when it was referenced. As shown in Figure 6, we can see that the value for a reference of DESTDIR was clearly not “/some/dir” and it was defined in the Makefile instead of the command line argument. Second, we questioned whether the definition of DESTDIR used “override” directives, which provide the 13 way to override a definition from command line arguments. So, we looked into Line 35 in the Makefile to check the definition of DESTDIR, which didn’t use the “override” directive. Third, we suspected that command line argument was not passed to the child make process. So, we checked the command line arguments for all make processes via CMD pages described in Section 4.3.2, and confirmed our suspicion – child make processes did not pass “DESTDIR=/some/dir” via command line arguments. Figure 6: Screenshot for GDB VAR2, identifying wrong value of DESTDIR reference. 5.3 APACHE VAR1 This bug is that the file server/export files generated during the build process is not deleted by a “make clean” nor a “make distclean”. We diagnose this problem using vizmake by the following steps: First, we know that the “make clean” error appears within the directory server, so we only run “vizmake clean” in the directory server. Second, we check the rm commands invoked by “make clean” to remove generated files, and find that “export files” is missing in the command line arguments of rm commands. Third, we check the command line specified in the Makefile from the web browser, and find that the text references a variable CLEAN TARGETS, which does not include “export files”. By this point, we find the root cause for this bug. 14 Figure 7: Screenshot for APACHE VAR1, identifying wrong value of CLEAN TARGETS reference. 5.4 GCC DEP1 This bug is that when the file io.h in GCC’s libfortran component is modified after an initial build, the modifications to io.h do not cause the libfortran component to be rebuilt. We diagnose this problem using vizmake through the following steps: First, we know “make” does not work correctly when building the libfortran component, so we only run vizmake in the build directory of libfortran. Second, we look at the dependency visualization page and search for io.h. We find that io.h is a missing dependency for several targets, as shown in Figure 8. Third, at the bottom of this dependency visualization page, we find the line number in the Makefile that specifies a particular rule with missing dependencies. Therefore, we can go back to the Makefile and edit the corresponding lines to add the missing dependency on io.h. 6 Conclusion In this project, we developed vizmake to facilitate debugging Makefiles via visualization. Our initial evaluation result is encouraging, which proves that vizmake is able to visually present the root causes for real bugs in real open source projects. Vizmake can be easily extended to visualize more information that helps debugging Makefiles, in addition to variable and dependency information. For example, we can visually highlight dead rules that are never 15 Figure 8: Screenshot for DEP VAR1, identifying missing dependency on io.h . executed, or we can detect parallel building bugs by examining the order of rules executed. In addition to bug finding, we envision that Vizmake can be applied to many other applications. First, it can be used in the process of writing Makefiles, so that developers can examine and understand a Makefile they write, reducing the chance of introducing bugs. Second, it can be used to help refactor Makefiles, as we demonstrated in the first test case in the case study. Third, it can be used to understand a big project’s structure for security analysis or other purposes. The source code of vizmake is available online: https://github.com/wenbinf/vizmake . References [1] ACM. SoftVis. http://www.st.unitrier.de/d̃iehl/softvis/org/index.php. [2] B. Adams. Co-evolution of Source Code and the Build System: Impact on the Introduction of AOSD in Legacy Systems. PhD thesis, Ghent University, 2008. [3] R. Bernstein. Debugging makefiles with remake. In 25th Large Installation System Administration Conference, 2011. [4] S. I. Feldman. Make — a program for maintaining computer programs. Software: Practice and Experience, 9:255–265, 1979. [5] A. Johnson and G. Holt. s is for source: The role of the build system in configuration management. Technical report, NVIDIA, 2005. [6] N. Jørgensen. Safeness of make-based incremental recompilation. FME 2002: Formal MethodsGetting IT Right, pages 126–145, 2002. [7] Michael Bostock. D3.js. http://d3js.org/. 16 [8] C. Parnin and A. Orso. Are automated debugging techniques actually helping programmers? In Proceedings of the 11th International Symposium on Software Testing and Analysis, 2011. [9] A. Tamrawi, H. A. Nguyen, H. V. Nguyen, and T. N. Nguyen. Build code analysis with symbolic evaluation. In Proceedings of the 2012 International Conference on Software Engineering, 2012. 17