首页 > 代码库 > 临时
临时
This work was partially performed when the first author was a visitor at NCSU, supported by a fellowship from the University of Pisa and MURST, Italy. This work was supported in part by NSF grant CCR-9320992 Design of a Toolset for Dynamic Analysis of Concurrent Java Programs Alessio Bechini Dipartimento di Ingegneria dell’Informazione Facoltà di Ingegneria - Università di Pisa via Diotisalvi, 2 56100 Pisa, Italy alessio@pical3.iet.unipi.it Kuo-Chung Tai Department of Computer Science North Carolina State University Raleigh, North Carolina 27695-7534, USA kct@csc.ncsu.edu Abstract The Java language supports the use of monitors, sockets, and remote method invocation for concurrent programming. Also, Java classes can be defined to simulate other types of concurrent constructs. However, concurrent Java programs, like other concurrent programs, are difficult to specify, design, code, test and debug. In this paper, we describe the design of a toolset, called JaDA (Java Dynamic Analyzer), that provides testing and debugging tools for concurrent Java programs. To collect run-time information or control program execution, JaDA requires transformation of a concurrent Java program into a slightly different Java program. We show that by modifying Java classes that support concurrent programming, Java application programs only need minor modifications. We also present a novel approach to managing threads that are needed for testing and debugging of concurrent Java programs. 1. Introduction The Java language supports concurrent programming in several forms. Java allows the use of monitors to express concurrency involving shared variables. For distributed programming, Java provides classes and packages for supporting the use of sockets and remote method invocation. Due to the nature of object-oriented programming, Java classes and packages can be defined to simulate other types of concurrent programming constructs. For example, a package defined in [7] contains classes for simulating the semaphore construct and various types of message passing constructs. Concurrent programs are difficult to specify, design, code, test and debug. One reason is the existence of race conditions due to the unpredictable rates of progress of concurrent processes. As a consequence, multiple executions of a concurrent program with the same input may exercise different sequences of interactions through shared variables or messages and may produce different results. This nondeterministic execution behavior makes it difficult to understand the behavior of concurrent programs. There are two general approaches to analyzing the behavior of a program. Static analysis of a program determines properties of the program without executing the program. Dynamic analysis of a program involves executing the program and analyzing the collected runtime information. In this paper, we describe the design of a toolset, called JaDA, for dynamic analysis of concurrent Java programs. In section 2, we give a review of dynamic analysis of concurrent programs. In section 3, we describe the goals of JaDA. In section 4, we present the architecture of JaDA and explain major design decisions. In section 5, we report the current status of JaDA’s implementation. Finally, we conclude this paper in section 6. 2. Review of dynamic analysis of concurrent programs Dynamic analysis of concurrent programs has two major types of analysis: testing and debugging, which are discussed in §2.1 and §2.2 respectively. Approaches to building dynamic analysis tools for concurrent programs are described in §2.3. An execution of a concurrent program exercises one or more synchronization events (or SYN-events). An actual or expected execution of a concurrent program can be characterized by its sequence of synchronization events, referred to as a synchronization sequence (or SYNsequence). The definitions of SYN-events and SYNsequences of a concurrent program are based on the concurrent programming constructs used in the program. How to define SYN-events and SYN-sequences for various concurrent-programming constructs has been shown in [3, 12, 13]. In the remainder of this paper, general issues on dynamic analysis of concurrent programs are discussed in terms of SYN-events and SYNsequences. By doing so, this discussion can be applied to different concurrent programming constructs. Let P be a concurrent program. A SYN-sequence is said to be feasible for P with input X if this SYNsequence can possibly be exercised during an execution of P with input X. A SYN-sequence is said to be valid for P with input X if, according to the specification of P, this SYN-sequence is expected to be the SYN-sequence of an execution of P with input X. 2.1 Testing of concurrent programs Below we briefly describe two basic approaches to testing concurrent programs. More details about these and other approaches can be found in [13]. Nondeterministic testing of a concurrent program P involves the following steps: 1) Select a set of inputs of P, 2) For each selected input X, execute P with X many times and examine the result of each execution. The purpose of multiple executions of P with input X is to exercise different feasible SYN-sequences and increase the chance of detecting faults. One technique for increasing the likelihood of exercising different SYN-sequences is to insert sleep (or delay) statements into P with the length of sleep randomly chosen. Deterministic testing of P involves the following steps: 1) Select a set of tests, each of the form (X, S), where X and S are an input and a SYN-sequence of P respectively. 2) For each selected test (X, S),– force a deterministic execution of P with input X according to S. This forced execution determines whether S is feasible for P with input X.– compare the actual and expected results (including the output, feasibility of S, and termination condition) of the forced execution. If the actual and expected results are different, a fault is detected. Deterministic testing allows carefully selected SYNsequences to be used to test specific portions or paths of P. Also, deterministic testing can detect the existence of invalid, feasible SYN-sequences of P and valid, infeasible SYN-sequences of P (Nondeterministic testing can only detect the existence of invalid, feasible SYN-sequences of P). 2.2 Debugging of concurrent programs Many debugging techniques for concurrent programs have been developed [9, 15]. Below we briefly describe debugging techniques that are related to JaDA: · Collection of SYN-sequences: The collected SYNsequences can be used for replay of previous executions (see below), and they can also be modified to generate new SYN-sequences for deterministic testing. · Replay of SYN-sequences: The replay of a previous execution of a concurrent program can be accomplished by deterministic testing of this program with the input and SYN-sequence of the execution. Thus, replay is a special case of deterministic testing with the use of feasible SYNsequences. · Vector timestamps: To identify the causal relationship between events in a collected SYN-sequence, vector timestamps for these events are needed. The computation of vector timestamps for message-passing programs is discussed in [5, 11]. The use of vector timestamps for concurrent programs using shared variables is described in [10]. How to compute vector timestamps for programs using both messages and shared variables, according to strong and weak happened-before relations, is shown in [1]. · Race analysis: Race analysis of a collected SYNsequence is to identify race conditions in the SYNsequence. The collected SYN-sequence of an execution of a message-passing program is a sequence of send and receive events. For a receive event r in a trace T, its race set is the set of messages in T that have a race with the message received at r and can be received at r during some possible executions of the same program with the same input. How to perform race analysis of SYNsequences of message-passing programs is shown in [14]. How to perform race analysis for programs using shared variables is shown in [1]. Note that the computation of vector timestamps and race analysis for a collected SYN-sequence can be performed either on-the-fly (i.e., during the collection of the SYN-sequence) or post-mortem (i.e., after the collection of the SYN-sequence). 2.3 Approaches to building dynamic analysis tools for concurrent programs To implement testing and debugging techniques for concurrent programs requires the construction of tools. Two basic approaches to building such tools for programs written in a concurrent language L are discussed below. An implementation-based approach is to modify one or more of the three components in the implementation of L: the compiler, the run-time system, and the operating system. These modifications enable the collection and forced execution of SYN-sequences during an execution of a program written in L. For example, many implementation-based debuggers allow the programmer to directly control execution by performing “scheduler-control”operations such as setting breakpoints, selecting the next running process, rearranging processes in various queues, and so on [9]. A recent example of this approach, in which the layered structure of the run-time system is used, is presented in [2]. A language-based approach for L has two steps. The first step is to define the format of SYN-sequences for L in terms of the synchronization constructs available in L. The second step is to develop program transformation tools for L in order to support SYN-sequence collection and execution. Language-based tools for supporting nondeterministic and deterministic testing of concurrent Ada programs and concurrent programs using the semaphore and monitor constructs have been implemented [3, 12]. 3. Goals of JaDA (Java Dynamic Analyzer) JaDA (Java Dynamic Analyzer) is a toolset for dynamic analysis of concurrent Java programs. The goals of JaDA are the following: a) to investigate the use of object-oriented technology for building dynamic analysis tools for concurrent Java programs, b) to provide an integrated and extensible environment that allows easy implementation of different dynamic analysis techniques for concurrent Java programs, and c) to support empirical studies of dynamic analysis techniques for concurrent Java programs. In the remainder of this section, we discuss two issues of JaDA from the user’s viewpoint. The next two sections describe the architecture and implementation of JaDA. Issue 1: Scope of concurrent Java programs As mentioned earlier, Java supports the use of monitors, sockets, and remote method invocation, and additional Java classes and packages can be defined to simulate other types of concurrent constructs. Thus, one design issue is the scope of concurrent Java programs covered by JaDA. Our decision is to let JaDA cover built-in and simulated concurrent constructs in Java. In the following discussion, packages that simulate concurrent constructs are referred to as “communication packages”.Issues 2: Transformation of concurrent Java programs Since JaDA does not access the implementation of Java, the only choice is to apply the transformation-based (or language-based) approach mentioned in §2.3. JaDA applies the following two types of transformation for dynamic analysis: a) If the user’s program uses built-in concurrent constructs (e.g., synchronized statements and monitors), it is transformed in order to support dynamic analysis. This type of transformation is illustrated in fig. 1B. b) If the user’s program uses simulated concurrent Fig. 1 - Representation of the structure of a Java program utilizing both built-in and communication packages. Diagrams B) and C) illustrate two types of transformation for dynamic analysis. The solution shown in C) allows a reduction of changes in the user’s program and makes most of such changes transparent to the programmer.?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????? ???????????????????? ???????????????????? ???????????????????? User’s Program???????? ???????? ???????? ???????? ???????? ???????? ?????????? ?????????? ?????????? Interaction between modules Transformation of the user’s program and communication packages Transformed part of a module?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? ?????? Legend???????????????? ???????????????? ???????????????? Original System???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????? ???????????????????????? ???????????????????????? ???????????????????????? Transformation of the user’s program only Packages Communication B) Built-in Packages and APIs A) C) constructs provided by communication packages, it is transformed and combined with transformed communication packages in order to support dynamic analysis. This type of transformation is illustrated in fig. 1C. (If the user’s program uses both built-in and simulated concurrent constructs, both types a) and b) of transformation need to be applied. For the sake of simplicity, we do not consider this case in the following discussion.) For a Java program using simulated concurrent constructs, type a) transformation could be applied. However, type a) transformation has the following disadvantages: · Type a) usually requires many changes in the user’s program and thus make the program difficult to understand. · Automatic transformation for type a) may be difficult to do for some simulated concurrent constructs. On the contrary, type b) transformation has the following advantages: · Type b) requires very few changes in the user’s program, due to the changes made in communication packages. · The original and transformed packages can be used in different phases of software development. So, for a Java program using simulated concurrent constructs, the transformation of type b) is more suitable than the other, and it allows to locate most of the changes in the communication package. In fig. 1, it is worth noticing how the interfaces between different modules are involved in the transformations of type a) and b). 4. Architecture of JaDA In this section we show the principal aspects of the structure of JaDA, focusing on the problems we have found and the solutions we have adopted. JaDA is intended to provide the following capabilities, which are discussed in §2.2: (Additional capabilities may be considered later.) a) collection of traces (or SYN-sequences) b) replay of traces c) computation of vector d) race analysis In JaDA, capabilities a), b) and c) are performed during program execution, based on the use of program transformation, as discussed in §3. The user’s program and communication packages are transformed only once. The transformed program and packages allow the user to choose one of three execution modes: normal execution, execution for collecting traces, and execution for replay. In addition, the transformed program and packages allow the user to specify whether computation of vector timestamps is performed during an execution. Capability d) is performed on collected traces in post-mortem fashion. One major design issue of JaDA is the use of centralized or distributed control for performing capabilities a), b) and c). Our decision is to use distributed control, in conjunction with partially ordered traces, in order to get better performance. Distributed control allows concurrent accesses to files for keeping a partially ordered trace. During replay of a partially ordered trace, distributed control allows concurrent events to be repeated in an arbitrary order and thus avoids unnecessary delay. Furthermore, distributed control supports the use of JaDA on multiple JVMs. Fig. 3 shows how the distribution of control is used in trace file management. Of course, in most of the cases a concurrent Java program is executed on a monoprocessor machine, so the benefit is negligible. However, if the program is spread out on JVMs on different physical machines, the improvement coming from a distributed control is significant. 4.1 Management of threads In Java each thread is created through an instance of class Thread, which has several static methods for managing threads. For dynamic analysis, we need to keep additional information for each thread. How to handle such information is a critical issue. Our solution is define a new class called JKThread such that each thread becomes an instance of class JKThread. In order to make all the transformations as transparent to the programmer as possible, we have decided to “sandwich”JKThread between the Thread class (belonging to the java.lang package) and the class with the thread code. Fig. 2A and fig. 2B show the insertion we are talking about. This is a good solution because we can create each thread directly as an instance of JKThread (instead of Thread). All the portions of the transformed program can access the additional data corresponding to the thread that is executing them in the following way (fig. 2C): the static currentThread() method in Thread returns a reference to a Thread object, but the new class JKThread extends it and so it is possible to apply a cast operator to get a reference to the current JKThread object (i.e., we can use “down-casting”).Class JKThread is implemented as follows. Constructors in class JKThread are expanded versions of corresponding constructors in class Thread. For example, below is JKThread’s constructor that has a Runnable object as a parameter: public JKThread (Runnable target, int numThreads, int whatIAm) { super(target); vectClock = new VectTS(numThreads); [ ... ] this.whatIAm = whatIAm; } In the above constructor, the first statement calls the corresponding constructor in class Thread and other statements assign values to variables for keeping additional information for a thread. The direct use of class JKThread, however, is impossible for some threads. Below are two examples: · The “main” thread of a Java program is created by the system and thus is not an instance of class JKThread. One solution to this problem is to disallow the main thread to perform any synchronization events. · In a Java GUI environment, threads are created by the system to perform operations associated with external events such as clicking on a button. Such threads are not instances of class JKThread. Considering these two facts, it is not always possible to use naively the JKThread class, but sometimes additional mechanisms are needed in order to use properly such class in different contexts. 4.2 Definition of event formats Every time we want to gather information about the events exercised during an execution, we must specify the types of the events and we must organize the event data in an appropriate format [13]. In our case, we can distinguish the following general types of event: synchronous send, synchronous receive, asynchronous send, asynchronous receive, read on a shared object, and write on a shared object. Considering the specific packages used by the program, many variations could be done within these basic categories. Since different types of events require different sets of data, it is impossible define a unique event format for all types of events. Below are some commonly used data associated with an event: · the thread executing the event · vector timestamp for the event · type of the event · name of the event · the SYN-object accessed by the event (e.g., the variable accessed by a read or write, or the channel accessed by a send or receive) One major design issue is whether the size of a vector timestamp is fixed (i.e., whether the number of threads in a Java program is fixed). JaDA allows the number of threads in a Java program to change during an execution. This decision makes the implementation of vector timestamps complicated, but allows more Java programs to use JaDA. We have defined a class called JKSynEvent, which contains information common to all types of events. For each type of event, we define a new class, which inherits from JKSynEvent and contains additional data needed for the event type. By doing so, we keep a hierarchical structure of event formats and allows the flexibility of defining new event formats if necessary. 4.3 Logging traces to file Dynamic analysis implies to deal with information about the events performed during an execution. This information must be structured in such a way to allow an easy and quick access. As mentioned earlier, JaDA uses Fig. 2 - Dynamic analysis requires additional information to be maintained for each thread (especially in the tracing phase). Such information can be placed in an instance of the class called JKThread, sandwiched between the classes of a specific thread and the class Thread (belonging to java.lang package). In the transformed program, a cast operator on the static method currentThread() of class Thread is used to access the additional thread information. Class diagram for a typical class implementing the body of a Java thread class Thread class Object class MyOwnThread interface Runnable java.lang class Thread class Object interface Runnable class MyOwnThread additional info about thread status and A) methods for managing it any object referred class JKThread java.lang B) Insertion of a class containing information and methods to deal with additional status variables for managing vector timestamps corresponding to the current thread through C) Getting a reference to the JKThread object a casting on the currentThread() method Thread object public static Thread currentThread(); JKThread object casting effect r = (JKThread) currentThread(); ... ... by a MyOwnThread object distributed control, in conjunction with partially ordered traces, for dynamic analysis. There are two different types of partially ordered traces [13]: a) based on threads b) based on SYN-objects. A thread-based partially ordered trace has one “partial”file (or trace) for each thread, while a SYN-object-based partially ordered trace has one “partial” file for each SYN-object. JaDA chooses to use SYN-object-based partially ordered traces. We define a class called JKSyncObjCtrl, which contains methods for accessing SYNobject-based partial files. For each SYN-object, an instance of JKSyncObjCtrl is created to control accesses to this SYN-object. Fig. 3 shows the interactions between threads and SYN-object-based partial files. Each partial file has the extension “.pef,” denoting “partial event file.” Also, each SYN-object-based partially ordered trace contains a header file to contain information related to the trace. 4.4 On-the-fly computation of vector timestamps Every time we want to associate information to events of an execution in order to understand their causal relationships, we have to compute a vector timestamp for each event [11]. This vector timestamp assignment operation can be done in two different ways: using either“on the fly” or “post mortem” algorithms [1]. Both these solutions have advantages and drawbacks, but we have chosen the first one for several reasons: · We do not consider real-time constraints, so we do not care about any “probe effect” and any“heisenbugs” [6]. Clearly, treating timestamp assignment“on the fly” augments the interference in the original program, but we have to think that a program, if correct, must behave in the expected way despite of any delay coming from the instrumentation. · Being the trace files usually big, it is convenient to assign timestamps during the execution, when the file must be used anyway, instead of manipulating the files twice (first for event logging, then for timestamp assignment). · The on-the-fly assignment avoids long waiting before starting race analysis. · The time overhead of timestamp computing and logging is distributed during the whole execution time. 4.5 Using templates for dealing with shared data We have already discussed the reasons for placing most of the transformations in the used packages instead Fig. 3 - A schematic view of the interaction among threads and partial files corresponding to SYN-objects. Each (transformed) thread has a private instance of the class JKThread, just to contain the additional data required by dynamic analysis. Moreover, the methods to manage the partial files are placed inside the JKSyncObjCtrl class, and every thread must use them for accessing the partial files. Thread #N Instance of class JKThread for thread #1 Instance of class JKThread for thread #2 Instance of class JKThread for thread #N is accessed through an instance Info in every partial file of class JKSyncObjCtrl Use of the M SYN-objects by the N transformed threads the transformed program Threads of Instance of class SObjName1.pef JKSyncObjCtrl Instance of class JKSyncObjCtrl Instance of class JKSyncObjCtrl Every thread uses an instance of class JKThread for additional local info necessary for dynamic analysis Code of Thread #1 Code of Thread #2 Code of Myname.hef SObjName2.pef SObjNameM.pef Header file Partial file #1 Partial file #2 Partial file #M Trace file system (partial ordering based on SYN-objects) of the program itself. This operation is permitted because of our attention toward high-level events. Sometimes, in our analysis we could have to consider accesses to shared data made through the direct use of basic Java synchronization capabilities. So, we have to remember that Java objects have an implicit lock which can be handled implicitly (through the statement synchronized) for assuring mutual exclusion in accessing them or specific parts of code (indications on how to use this and other Java basic concurrent constructs can be found in [8] ). In dealing with accesses to shared data in the analyzed program, we must insert additional code directly within the original program. This insertion must provide the tracing and replay capabilities and must encompass the variables and the procedures for treating vector timestamps, according with the algorithms described in [1]. Even in these cases, the transformed program should keep visible the original control structure. For assuring such characteristic, we have decided to design some templates for the additional code to place where shared accesses are present. In our approach, we assume that a synchronized class is used for hiding shared data, and its methods provide read and write operations on them (i.e., a synchronized class is used as a monitor-like construct). This way of coping with the problem of shared data is very similar to that shown in [3]. 4.6 Race analysis of collected traces Once a trace has been collected, we can use it for verifying properties of the execution. A particular type of inspection of the characteristics of an execution can be done through race analysis, as previously mentioned in §2.2. As Java threads can interact using both message passing and shared variables, in JaDA it is possible to compute race sets for both receive and read events. This operation is not particularly difficult, since the trace contains, for each event, the relative vector timestamp. Moreover, there is the possibility of using two different relations for causality detection: weak and strong happened-before [1]. Of course, the general algorithms presented in [14] and [1] had to be adapted to the particular structure of the Java environment, tailoring them to the peculiarities of the concurrent constructs and SYNobjects used in the program. 5. Current implementation of JaDA At present JaDA implementation covers the basic features for shared data access, as described in §4.5, and some of the concurrent constructs in package Synchronization in [7]. Without describing in depth the techniques used for the transformation of the package, we can say that the new code inserted in each class try to leave clearly visible the original one, and that the added portions try to mimic the structure and the behavior of the original code. It could be interesting to underline that the code for tracing purposes is placed after the lines corresponding to the event, despite that for replay, which has to be put directly before the event. Moreover, as a consequence of the application of the philosophy of hiding the transformations to the original program, the number and the types of the parameters in every public method must remain the same. JaDA is composed by the following three packages: · JaDAkernel: it contains classes and interfaces for supporting basic functions in dynamic analysis and providing services needed by other JaDA packages. · JaDAmsg: it contains classes and interfaces that implement various message-passing constructs with dynamic analysis capabilities. · JaDAvar: it contains classes and interfaces that implement various variable-sharing constructs with dynamic analysis capabilities. Currently, we have implemented classes in package JaDAkernel to provide functions mentioned in §4.1 through §4.4. For package JaDAmsg, we have revised classes in [7] to add tracing and replay capabilities. For package JaDAvar, we plan to include classes and interfaces that simulate various types of semaphores and monitors. Considering the goals of JaDA, an academic study could be limited to a little portion of a single package, but in this way there is no serious possibility to do relevant empirical experiments using JaDA: the larger the scope of the tool, the better the benefits we can have through it. 6. Conclusions In recent years, several approaches were proposed for analyzing, testing and debugging concurrent programs. Since Java is becoming a major language for writing concurrent programs, static and dynamic analysis of concurrent Java programs are important research topics. Some issues on static analysis of concurrent Java programs are discussed in [4]. In this paper, we have described the design (and some implementation details) of JaDA, which provides testing and debugging tools for concurrent Java programs. To collect run-time information or control program execution, JaDA requires transformation of a concurrent Java program into a slightly different Java program. We have shown that by modifying Java classes that support concurrent programming, Java application programs only need minor modifications. We have also presented a novel approach to managing threads that are needed for testing and debugging of concurrent Java programs. As mentioned in §3, JaDA is intended to accomplish the following goals for concurrent Java programs: a) to investigate the use of object-oriented technology for building dynamic analysis tools, b) to provide an integrated and extensible environment that allows easy implementation of different dynamic analysis techniques, and c) to support empirical studies of dynamic analysis techniques. In this paper, we have addressed major issues related to the first two goals. We have made significant progress in the implementation of JaDA. Soon we will use JaDA to carry out some empirical studies of testing and debugging of concurrent Java programs. We will also investigate extensions of JaDA to increase the quality and reliability of concurrent Java programs. Acknowledgments We want to express our grateful acknowledgments to Robert Harris, Bengi Karacali, Naveen Sarabu, and Jun Zhou for their contributions to the design and implementation of JaDA. We also wish to thank the anonymous reviewers for their helpful comments on an earlier version of this paper. References [1] A. Bechini and K. C. Tai, “Timestamps for Programs Using Messages and Shared Variables,” in Proc. of 18th IEEE Inter. Conf. on Distributed Computing Systems, May 1998. [2] A. Bechini, J. Cutajar, and C. A. Prete, “A Tool for Testing of Parallel and Distributed Programs in Message Passing Environments,” in Proc. of 9th Mediterranean Electrotechnical Conf. , May 1998 [3] R. H. Carver and K. C. Tai, “Replay and Testing for Concurrent Programs,” IEEE Software, March 1991, pp.66-74 [4] J. C. Corbett, “Constructing Compact Models of Concurrent Java Programs,” in Proc. of ACM Inter. Symp. Software, Testing and Analysis (ACM Software Engineering Notes, Vol. 23, No. 2, March 1998) pp. 1-11 [5] C. J. Fidge, “Logical Time in Distributed Systems,” IEEE Computer, Aug. 1991, pp. 28-33. [6] J. Gait, “A Probe Effect in Concurrent Programs,” Software-Practice and Experience, Vol.16, No. 3, March 1986, pp. 225-233 [7] S. J. Hartley, “Concurrent Programming: The Java Programming Language,” Oxford University Press, 1998. [8] D. Lea, “Concurrent Programming in Java: Design Principles and Patterns,” Addison Wesley, 1997 [9] C. E. McDowell and D. P. Helmbold, “Debugging Concurrent Programs,” ACM Computing Surveys, Vol. 21, No. 4, Dec. 1989, pp. 593-22 [10] R. H. B. Netzer, “Optimal Tracing and Replay for Debugging Shared-Memory Parallel Programs,” in Proc. ACM/ONR Workshop on Parallel and Distributed Debugging, 1993, pp. 1-11 [11] R. Schwartz, and F. Mattern, “Detecting Causal Relationships in Distributed Computations: in Search of the Holy Grail,” Distributed Computing, Vol. 7, 1994, pp. 149-174. [12] K. C. Tai, R. H. Carver, and E. E. Obaid, “Debugging Concurrent Ada Programs by Deterministic Execution,”IEEE Trans. Soft. Eng., Vol. 17, No. 1, Jan. 1991, pp. 45-63 [13] K. C. Tai and R. H. Carver, “Testing of Distributed Programs”, chapter 33 of Handbook of Parallel and Distributed Computing, edited by A. Zoyama, McGraw-Hill, 1996, pp. 955-978 [14] K. C. Tai, “Race Analysis of Traces of Asynchronous Message-Passing Programs,” Proc. 17th IEEE Inter. Conf. Distributed Computing Systems, 1997, pp. 261-268 [15] J. J. P. Tsai and S. J. H. Yang, eds., “Monitoring and Debugging of Distributed Real-Time Systems,” IEEE Computer Society, 1995.
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。