• No results found

Automatic Generation of Simulation Models from Designs

N/A
N/A
Protected

Academic year: 2021

Share "Automatic Generation of Simulation Models from Designs"

Copied!
73
0
0

Loading.... (view fulltext now)

Full text

(1)

Institutionen för datavetenskap

Department of Computer and Information Science

Master’s Thesis

Automatic Generation of Simulation

Models From Designs

Erik Axling

Reg Nr: LITH-IDA-EX--07/064--SE Linköping 2007

Department of Computer and Information Science Linköpings universitet

(2)
(3)

Institutionen för datavetenskap

Department of Computer and Information Science

Master’s Thesis

Automatic Generation of Simulation

Models From Designs

Erik Axling

Reg Nr: LITH-IDA-EX--07/064--SE Linköping 2007

Supervisor: Mattias Svanström

Enea Services Linköping

Examiner: Christoph Kessler

ida, Linköping University

Department of Computer and Information Science Linköpings universitet

(4)
(5)

Avdelning, Institution Division, Department

Software and Systems

Department of Computer and Information Science Linköpings universitet

SE-581 83 Linköping, Sweden

Datum Date 2007-12-05 Språk Language  Svenska/Swedish  Engelska/English   Rapporttyp Report category  Licentiatavhandling  Examensarbete  C-uppsats  D-uppsats  Övrig rapport  

URL för elektronisk version

http://urn.kb.se/resolve?urn=urn:nbn:se:liu:diva-10409

ISBNISRN

LITH-IDA-EX--07/064--SE

Serietitel och serienummer Title of series, numbering

ISSN

Titel Title

Automatisk generering av simuleringsmodeller från designer Automatic Generation of Simulation Models from Designs

Författare Author

Erik Axling

Sammanfattning Abstract

When working with embedded systems, secure and fast applications are desired. To achieve this the applications needs to be analyzed and optimized so that they will not be deadlocked or communicate inefficiently. For this purpose an analysis program that can track communications, deadlocks and response times is needed. Operating System Embedded, OSE, is a wide spread real-time operating system that is used in embedded systems. OSE-applications are excellent candidates for analysis and there exists such a tool, VirtualTime, for that purpose. To analyze an OSE-application a model needs to be written that VirtualTime can analyze. This takes up time and effort as the models can require a lot of work to write.

In this thesis we have investigated and implemented a prototype that trans-lates OSE-application code into VirtualTime simulation model code. We used the transformation tool TXL to translate communication and timing behaviors. In the translation one needs to preserve the communication and timing behavior and throw away other unnecessary code in the OSE-application. This complicates the translation and sophisticated methods like backward slicing might be necessary. A proposed method in this thesis could help with the problem.

Nyckelord

(6)
(7)

Abstract

When working with embedded systems, secure and fast applications are desired. To achieve this the applications needs to be analyzed and optimized so that they will not be deadlocked or communicate inefficiently. For this purpose an analysis program that can track communications, deadlocks and response times is needed. Operating System Embedded, OSE, is a wide spread real-time operating system that is used in embedded systems. OSE-applications are excellent candidates for analysis and there exists such a tool, VirtualTime, for that purpose. To analyze an OSE-application a model needs to be written that VirtualTime can analyze. This takes up time and effort as the models can require a lot of work to write.

In this thesis we have investigated and implemented a prototype that trans-lates OSE-application code into VirtualTime simulation model code. We used the transformation tool TXL to translate communication and timing behaviors. In the translation one needs to preserve the communication and timing behavior and throw away other unnecessary code in the OSE-application. This complicates the translation and sophisticated methods like backward slicing might be necessary. A proposed method in this thesis could help with the problem.

(8)
(9)

Acknowledgments

Here I would like to thank all the people that has stood by me in my work in the thesis. I have had an enjoyable time and I have received a lot of experience.

I would like to thank all the people at Enea and especially my supervisor Mat-tias Svanström that has encouraged me throughout the work. I would also like to thank my examiner at IDA, Christoph Kessler, for answering my questions and giving me feedback. A great thanks also goes out to James R. Cordy for helping me with TXL and answering a lot of questions.

Last but not least I would like to thank my family for their support and en-couragement throughout the entire thesis.

(10)
(11)

Contents

1 Introduction 1 1.1 Background . . . 1 1.2 Purpose . . . 1 1.3 Method . . . 1 1.4 Limitations . . . 2 1.5 Thesis Disposition . . . 2 2 Background 3 2.1 Operating System Embedded . . . 3

2.1.1 Architecture . . . 3

2.1.2 Processes and Communication . . . 4

2.1.3 Example of OSE-code . . . 5

2.2 TXL . . . 6

2.2.1 Working with Grammars . . . 7

2.2.2 Rules and Functions . . . 8

2.3 VirtualTime . . . 11

2.3.1 Example . . . 13

3 Problem Definition 15 3.1 Generating simulation models from OSE-application code . . . 15

3.1.1 Performance Requirements . . . 18

3.2 Limitations . . . 18

3.3 Concise Problem Formulation . . . 18

4 Straightforward Method 19 4.1 Algorithm . . . 19

4.2 TXL . . . 21

4.3 Testing and Results . . . 23

4.4 Why does it not work? . . . 26

5 Improved Method 27 5.1 Backward Slicing . . . 27

5.1.1 System Dependence Graph . . . 28

5.1.2 Backward Slicing with TXL . . . 30

5.2 Algorithm . . . 31

(12)

6 Discussion 33

6.1 General Discussion . . . 33

6.2 Discarded Techniques and Methods . . . 34

6.3 Related Work . . . 34

6.4 Future Work . . . 35

Bibliography 37 A TXL Source Code 39 A.1 The Main Program File . . . 39

A.2 Transform Functions File . . . 43

B Test Files 50 B.1 Test One . . . 50 B.1.1 Test Input . . . 50 B.1.2 Test Output . . . 51 B.2 Test Two . . . 53 B.2.1 Test Input . . . 53 B.2.2 Test Output . . . 54 B.3 Test Three . . . 55 B.3.1 Test Input . . . 55 B.3.2 Test Output . . . 56 B.4 Test Four . . . 57 B.4.1 Test Input . . . 57 B.4.2 Test Output . . . 58 B.5 Test Five . . . 58 B.5.1 Test Input . . . 59 B.5.2 Test Output . . . 59

(13)

Chapter 1

Introduction

1.1

Background

Operating System Embedded, OSE, is a widely used real-time operating system that is used in mobile phones around the world. OSE is a message based operating system and in advanced applications it can be arduos to know how the commu-nication will affect the efficiency of the application. To analyze the interactions between processes there exists an application that is called VirtualTime that can model the communication between processes in OSE. Using VirtualTime you can find performance bottlenecks or other aspects where efficiency can be improved in an application. This motivates the creation of a tool that can transform OSE-application code to a VirtualTime simulation model. The possibility of creating a transformation tool is investigated in this thesis.

1.2

Purpose

To be able to model an OSE-application for analysis in VirtualTime one needs to write a completely new VirtualTime application and this might take a lot of time. This thesis will investigate if it is possible to make a translator between the OSE application code and the VirtualTime model code. It will also examine how this would be done and which tools that would be necessary for this. An implementa-tion will be made with possible methods for constructing the translator.

1.3

Method

Two methods were investigated in this thesis to solve the problem of making a translator between OSE-applications and VirtualTime models. The straightfor-ward method was implemented using TXL to translate relevant commands in the OSE-applications to their VirtualTime equivalents. This was not as powerful as we thought so we proposed an improved method that uses backward slicing. There

(14)

was not enough time to implement the improved method so this was left to future work.

1.4

Limitations

Achieving a fully automatic translator is a very difficult problem and might even be impossible. Therefore one clear limitation to the project is that the system will not be fully automatic. The user will have to manually edit the application after the translation has been performed.

1.5

Thesis Disposition

Here a description of each chapter is presented:

• Chapter 1, Introduction, here a short description of the background, purpose,

method and limitations of the thesis is given.

• Chapter 2, Background, here a thorough description of the background of

the thesis is given. OSE, TXL and VirtualTime will be explained so that the reader has an foundation for further studies of the thesis.

• Chapter 3, Problem Definition, a concise definition of the problem will be

given here.

• Chapter 4, Straightforward Method, a description of the straightforward

implementation of the translator using TXL is explained here.

• Chapter 5, Improved Method, the proposed, but not implemented method

using backward slicing, is presented here.

• Chapter 6, Discussion, the authors discussions about the general work in the

thesis and the outcome is presented here. A review of related work and the future work needed is also presented in this chapter.

(15)

Chapter 2

Background

Here in the background section we will go through the relevant theory needed to understand the rest of the thesis. A requirement is that the reader have a basic knowledge of compiler theory. OSE, TXL and VirtualTime will be explained so that the reader will understand the implementation.

2.1

Operating System Embedded

In this section we will attain a very basic presentation of Operating System Embed-ded, OSE, so that the reader will be able to understand the problem definition and the proposed methods. An introduction to the architecture of OSE will be given and the relevant code for interprocedural communication in OSE will be discussed.

2.1.1

Architecture

OSE is a real-time operating system for embedded systems, see [9]. OSE is also a multitasking operating system and has duties that mainly range in three cate-gories, these are resource mangagement, time management and interprocess com-munication. The OSE real-time operating system becomes complete with a kernel, a closely related interface library and a number of header files, which define the user interface. The kernel has four different levels. Level A is the level with the smallest set of system calls allowed in any OSE-kernel, see [9]. Each level up to D includes more and more functionality. The kernel is the most important part of OSE as it contains all the necessary system calls a user needs.

The interface library represents the entire user process interface. This is what the user uses to perform system calls. The user communicates with the interface library which in turn communicates with the kernel using traps or similar mecha-nisms. The interface library contains the startup entry point for the kernel system.

(16)

The user interface for OSE is defined as C header files. The relevant header files need to be included in user application to be able to communicate with the OSE-kernel.

2.1.2

Processes and Communication

Processes in OSE are either static or dynamic [9]. Static processes are created at system start by the kernel. A static process always exists while the system "lives". A static process cannot be killed, at least not if it is started by the kernel. Dynamic processes can, unlike static processes, be created or killed at any time during run-time. Dynamic processes could be used to launch several processes running with the same code.

A process can be in one of three states, running, ready or waiting. If a process is running it is executed by the CPU. If it is ready it is placed in a ready queue. When a CPU is to execute a process, after for example a context switch, it ex-ecutes the first process in the ready queue. A process can be waiting if it is to receive a signal or maybe is waiting for a delay. When it is done waiting it is either sent to the running or ready state depending on its priority relative other processes.

Interprocess communication is done mainly via message passing. In OSE a message is called a signal. A signal is sent from one process to another. The process that sends a signal needs to know which process it wants to communicate with. Before sending a signal the process needs to allocate a memory buffer. The buffer contains information about the signal, for example its owner, the sender and the receivers identity. Only the owner of the buffer can perform operations on it and a process can only get ownership of a buffer by allocating it or receiving it. When a process sends a signal and the receiving process receives the signal the ownership of the buffer is switched to the receiving process.

Semaphores and fast semaphores can be used for synchronization. A fast semaphore is owned by one process. This process will perform a wait opera-tion on the fast semaphore, which only the owner can do, and it will be blocked. When the wait operation is performed on the fast semaphore a counter will be decremented and the value of the fast semaphore will be negative. When another process, of lower priority, performs a signal operation on the fast semaphore and the semaphore value becomes positive it will be pre-empted and the process own-ing the fast semaphore will be executed immediately. An ordinary semaphore is not owned by any process and all processes can issue a wait operation on an or-dinary semaphore. All processes waiting for a semaphore will be put in a queue and one process will be allowed to execute the code protected by the semaphore. When a process has executed the protected code a signal operation is done on the semaphore and the first process in the queue for the semaphore is allowed to execute the protected code.

(17)

2.1 Operating System Embedded 5

2.1.3

Example of OSE-code

In this section we will see a code snippet from a ping pong program that uses interprocess communication and various OSE-commands. This code example will be seen later on in the thesis but the OSE aspects will be explained here:

OSE code 1 #include "ose.h" 2 #include "stdio.h" 3 #include "shell.h" 4 #include "string.h" 5 6 PROCESS process_1_; 7 PROCESS process_2_; 8

9 static const SIGSELECT any_sig[] = {0}; 10 11 struct Ball { 12 SIGSELECT sig_no; 13 int ball_no; 14 }; 15 16 union SIGNAL { 17 SIGSELECT sig_no;

18 struct Ball ball; 19 };

20

21 OS_PROCESS(process_1) { 22 union SIGNAL *sig; 23 union SIGNAL *pong_sig; 24

25 process_1_ = current_process(); 26 for( ; ; ) {

27 sig = receive(any_sig);

28 pong_sig = alloc(sizeof(struct Ball), 4); 29 send( &pong_sig, process_2_);

30 free_buf(&sig); 31 }

32 } 33

34 OS_PROCESS(process_2) { 35 union SIGNAL *sig;

36 process_2_ = current_process(); 37 for( ; ; ) {

38 sig = alloc( sizeof(struct Ball), 4); 39 sig->ball.ball_no = 2;

(18)

41 sig = receive(any_sig); 42 free_buf(&sig);

43 delay(5000); 44 }

45 }

This OSE-application can be described as a ping pong program where

pro-cess_2 is sending a "ping"-signal which process_1 responds to after receiving the

"ping"-signal. We see here that we include ose.h which is the header file for the main system calls in OSE. We explicitly declare process_1_ and process_2_ and then assign these their process identities using current_process(). This needs to be done because otherwise it is difficult to communicate between the processes as they do not know each others identities. They could find out the identities using a system call called hunt but this is not explained here.

A signal selection number that is used in the receive command to select which signals to receive is declared. When it is assigned to zero it means that all signals can be received. The array can specify different signal selection numbers if the receiving process wants to receive specific signals. A Ball struct is declared and included into the signal definition. This means that the Ball struct will be sent with the signal. OS_PROCESS(process_1) is the entry point for process_1. Inside the process bodies we declare the signals that is to be sent and received.

Within the infinite loop the signals are received with the receive command, they are allocated with the alloc command or they are sent with the send command. The alloc command allocates a buffer for the signal and sets the process as the owner. When the signal is sent the ownership is transferred to the receiver. When a buffer is not used it is freed using the free_buf command. When we want a process to wait or sleep for a while we use the delay command.

2.2

TXL

TXL is a transformation system that is designed to handle source to source trans-formation tasks and rapid language prototyping. TXL is a fully functional and rule based language that uses pattern matching for transformations. To be able to parse a language TXL needs a grammar of that language. There exist a number of predefined grammars at [3]. The grammars are defined with a top production that can have non-terminal symbols and terminal symbols. Each non-terminal symbol has its own production that can consist of non-terminal and terminal symbols.

TXL uses a general parsing strategy of top-down parsing with full backtrack-ing, [11], this enables grammars with either left or right recursion. To support both left and right recursion a hybrid of bottom-up parsing heuristic and a set of efficient built-in repetition primitives is used. TXL can handle ambiguous

(19)

2.2 TXL 7

grammars by exploring alternatives that are given in the grammar. TXL han-dles user-oriented grammars better than implementation style grammars used for traditional compiler-style grammars. User-oriented grammars are styled to be more easily read while compiler-style grammars are optimized to be used with a computer.

2.2.1

Working with Grammars

TXL grammars always has a top production called program. Terminals and non-terminals, when used in productions, are denoted with brackets like [name]. When a nontermninal is defined it is preferably written with lower case letters but upper case letters could also be used. A small example is shown below:

Grammar Example 1 define program 2 [id] 3 | [number] 4 | [find_statement] 5 end define 6 7 define find_statement 8 find [id] 9 | find [number] 10 end define

In this example we can see the built in terminals id and number. Here id is de-fined by all lower case and upper case letters, numbers and underscore, the typical definition of for example C identifiers. An identifier cannot start with a number. We see that numbers means all numbers. In this case the grammar would be able to handle input like any number, identifier or find statement with either a number or identifier as argument.

If we would like to have lists or repeating symbols we can use the list built-in function or the repeat built-in function. If we would, e.g., build an argument list with one or more arguments we could do it like this:

Repeat Example

1 define argument_list 2 [id] , [repeat id] 3 end define

With repeat there can be zero or more elements in the list. With list there can be one or more elements in the list. An example with list is given below.

(20)

1 define word

2 [list stringlit] 3 end define

This example defines a word which consists of a string with one or more string characters. If we want zero or one element in our code we can use the built-in opt function. This would work well with for example pointers:

Opt Example

1 define arguments

2 [opt &] [id], [opt &] [id] 3 end define

Tokens are what the lexical analyzer uses to characterize input into categories that the parser understands. Predefined tokens exist and have been used in previ-ous examples. For example id, number or stringlit are predefined tokens. Tokens are defined within the tokens statements. Regular expressions are used to define the tokens. There exist many types of patterns and they are explained in detail in [11]. A small example is shown below:

Tokens Example

1 tokens

2 hexnumber "0[Xx][\dABCDEFabcdef]+" 3 end tokens

We see in the example above that [hexnumber ] is defined to start with a zero and then either an upper or lower case x. Then follows one or more of either a number, defined by \d which means any digit between zero or nine, or a upper or lower case a, b, c, d, e or f. A basic understanding of how to work with grammars is needed when it comes to making transformations. TXL parses the input into a tree form that is based on the grammar.

2.2.2

Rules and Functions

When we transform something in TXL it needs to be replaced with something that is of the same type as that which is to be replaced. It means that both the replacement and what is to be replaced needs to be in the same grammar tree. Look at the example below:

Rules Example

1 ...

2 define all_statements 3 ...

(21)

2.2 TXL 9 5 | [if_statement] 6 end define 7 define if_statement 8 if ( [logical_expression] ) { 9 [compound_body] 10 } 11 end define 12 define statement 13 [for_loop] 14 | [while_loop] 15 end define 16 define for_loop

17 for ( [assignement_expression]; [expression]; [expression] ) { 18 [compound_body] 19 } 20 end define 21 define while_loop 22 while ( logical_expression ) { 23 [compound_body] 24 } 25 end define 26 ...

Here we see, in this simplified example, that both for_statement and while_loop belongs to and will be put under statement in the grammar tree. If we want to exchange a for loop with a while loop this would be okay. But if we want to change a for loop to an if statement this would not work, provided that the type of the replacement would be [statement] as they are not in the same grammatical tree. We need to construct our grammar so that we can transform as we want. If we are using a pre-existing grammar we can redefine the existing grammar using the redefine command. The command redefine can be used to change the existing grammar or add to the existing one.

Rules and functions work differently but look pretty similar. They both have the pattern to be replaced and the replacement. But rules are applied to the entire scope that it is given. A function is applied to the first match of the pattern within its scope. If you apply a rule to a scope that does not have a replacement that differs from the applied pattern, i.e., the transformation itself matches the pattern of the rule, then the rule will be applied to itself in infinity as it will never leave the first match. More will be explained in the section about patterns and replacements. An example of how a rule and a function can be written is given below:

Transform Example

1 rule transformDelay

(22)

3 delay( Num1 [number] ) ; 4 by

5 vt_use_cycles( Num1 ) ; 6 end rule

This is quite similar to one of the transformations that was used in the cur-rent thesis work. Here we transform the delay command into the vt_use_cycles command. The delay command has a parameter that is often a number. This number is assigned to a TXL-parameter called Num1, we can see this in line three in the example. This Num1 is then used in the replacement as a parameter to the

vt_use_cycles command.

Patterns and Replacements

All rules and functions that transform anything have patterns and replacements. The pattern states what is to be replaced and is always given a type. In the earlier transform example the type for the delay command pattern was

expres-sion_statement. This indicates which type the pattern has related to the

gram-mar. Both the replacement and the pattern needs to be of the same grammatical type as is given. When a new variable is introduced in the pattern, like for example

Num1 in the example above, it needs to be explicitly typed, Num1 is for example

typed with [number ]. When it has been declared it does not need to be declared again.

Two important constructions in TXL is the deconstructor and the constructor. The deconstructor is used to pick apart and identify sub trees in the pattern. We can see in the example below that there is a function call with a parameter list in the pattern. But we only want the pattern to match a parameter list with two parameters. Then we can use a deconstructor.

Deconstruct Example

1 define transformFunctionCall 2 replace [function_call]

3 functionName [id] ( parameters [parameterList] ) ; 4 deconstruct parameters

5 parameter1 [parameter] , parameter2 [parameter] 6 by

7 functionName( parameter2, parameter1 ); 8 end define

We could of course just write directly in the pattern that we should match only two parameters and deconstructors should only be used when the patterns get too complicated. But the example shows the principle of the deconstructor.

The constructor should be used when we want to use something that does not exist, like a new type of statement that still matches the type of the pattern. This

(23)

2.3 VirtualTime 11

sounds abstract but what we are doing is that we are creating a new subtree and we can use this in the replacement, if it is of the right type. In the example below we have a sequence of numbers and we want to shift the first number to the last place.

Construct Example

1 function shiftSequence

2 replace [repeat number]

3 firstNum [number] restNum [repeat number] 4 construct NewSequence [repeat number]

5 restNum firstNum 6 by

7 NewSequence 8 end function

We match any number sequence and save the first number and the rest of the numbers in separate parameters. Then we construct a new sequence from these two numbers and "save" it in NewSequence. This is then used as the replacement.

These were the basics of TXL, there is of course a lot more to learn but perhaps the reader has got a basic understanding of how the code has been written and how one must think when constructing a method. If the interested reader wants to learn more about TXL a good start would be the web page at reference [3], which is http://www.txl.ca.

2.3

VirtualTime

VirtualTime is an analysis tool that is used for creating simulations of complex real-time systems. It can be used to create simulation models of OSE-applications that can be utilized to maximize efficiency of the applications. The simulation models are written as ANSI C applications and they are compiled into a separate program. This program generates a log file that can be analyzed by a VirtualTime tool called vt_analyze. From vt_analyze a HTML-page is generated that shows important statistics from the analysis. Each process message queue, CPU usage and response times is shown from the analysis. An example of the log output is shown in figure 2.1. This figure illustrates the process usage of the CPU by each process.

Each VirtualTime model needs a main function. In this main function the simulation is initialized, one CPU or more is created, each process is declared and set to a CPU and the simulation is started. Each process is defined outside of the main function. They contain communication commands, synchronization commands or time abstraction commands. The VirtualTime-commands mimic the OSE-commands and the code will be similar to the OSE-code.

(24)

Figure 2.1. In this figure we see a log output from VirtualTime showing the CPU usage

(25)

2.3 VirtualTime 13

2.3.1

Example

Typical commands needed for a VirtualTime simulation model is for example

vt_send, vt_receive or vt_use_cycles. These correspond to the OSE-commands send, receive and delay. So if we would like to construct a ping pong program

similar to the example in the section about OSE it would look like this:

VirtualTime Example 1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <vt_ose.h> 4 vt_cpu_t *cpu; 5 vt_process_t *pong_;

6 void pong (vt_process_t *me); 7 vt_process_t *ping_;

8 void ping (vt_process_t *me); 9

10 int main () {

11 vt_init_simulation (); 12 cpu = vt_create_cpu ("CPU");

13 pong_ = vt_create_process (VT_PRI_PROC,

14 "pong", &pong, VT_PRIORITY_MIN, cpu); 15 ping_ = vt_create_process (VT_PRI_PROC,

16 "ping", &ping, VT_PRIORITY_MIN, cpu); 17 vt_run_simulation (100000);

18 vt_exit_simulation (); 19 return 0;

20 }

21 void pong (vt_process_t *me) { 22 vt_signal_t sig;

23 vt_signal_t pong_sig; 24 for (;;) {

25 vt_receive (&sig);

26 vt_send (&pong_sig, ping_); 27 }

28 }

29 void ping (vt_process_t *me) { 30 vt_signal_t sig;

31 for (;;) {

32 vt_send (&sig, pong_); 33 vt_receive (&sig); 34 vt_use_cycles (5000); 35 }

(26)

Here we see a VirtualTime simulation model. First the VirtualTime header file, vt_ose.h is included. The header file serves as the interface to the VirtualTime operations. Then we declare all the processes and CPU’s that we need in the sim-ulation. For a ping pong simulation we need two processes so we declare two cess identities, vt_process_t *pong_ and vt_process_t *ping_, and two pro-cess prototypes. The prototypes should be declared so that we can tie the propro-cess body to a process identity in the main function. In the main function we initialize the simulation using vt_init_simulation(). The vt_init_simulation() is also the first VirtualTime command that should be executed within the main function except log utility functions. Then a CPU is created by vt_create_cpu along with processes, which in turn are created with the vt_create_process command. These commands tie the process identity to the actual process body. We also give each process a name that is used in the output from vt_analyze. Then the simu-lation is run and stopped within the main function. The simusimu-lation is run for the amount of time given in the parameter for the vt_run_simulation command.

In each of the process bodies we can see that signals are declared using the type vt_signal_t. These signals are used in conjunction with vt_send and vt_receive. The commands vt_send and vt_receive are used for simulating the interprocess communication. vt_send needs to know which process the signal is sent to. In both the send commands in the processes we give the other process’ identity as the parameter. This means that the message swill be sent to the other process. vt_receive needs a signal as parameter to store the incoming signal. Then we use vt_use_cycles to process computation cycles, this is VirtualTime’s equivalent to delay in OSE. We could also use vt_use_cycles to abstract heavy computation in various parts of the OSE program that does not use any commu-nication or synchronization commands.

The example that we have reviewed is a short description of how a VirtualTime simulation model can be written. It does not take all commands or possibilities in VirtualTime into consideration. More about VirtualTime can be read in [15, 16].

(27)

Chapter 3

Problem Definition

3.1

Generating simulation models from

OSE-app-lication code

The basic problem in the thesis is the automatic translation of OSE-application code to a simulation model in VirtualTime. The simulation model in VirtualTime is expressed in ANSI C code which is the same format as the OSE-application code. The translation is thus from ANSI C code to ANSI C code and this is what is called a source to source transformation.

As VirtualTime can only be used to model certain aspects of an OSE-application not everything should be translated. Some code snippets in the OSE-application then needs to be removed. This poses a problem, as it is difficult to know which parts of the code are relevant for translating into VirtualTime code. There might be complex dependencies which can be destroyed in the translation. A typical example of an OSE-process and its analogy in VirtualTime is presented here:

OSE code

1

2 PROCESS process_1_; 3 PROCESS process_2_;

4 static const SIGSELECT anysig[] = {0}; 5 6 union SIGNAL { 7 SIGSELECT sig_no; 8 }; 9 10 OS_PROCESS( process_1 ) { 11 union SIGNAL *signal;

12 process_1_ = current_process(); 13 while( true ) {

14 sig = receive( any_sig );

(28)

15 send( sig, process_2_); 16 delay( 500 ); 17 } 18 } 19 20 OS_PROCESS( process_2 ) { 21 union SIGNAL *signal;

22 process_2_ = current_process(); 23 while( true ) {

24 delay( 100 );

25 send( sig, process_1_); 26 sig = receive( any_sig ); 27 }

28 }

It is important to note that this is not a fully working code snippet and some of the code has been abstracted. See the background chapter for a full example of the above code. The VirtualTime equivalent is presented here:

VirtualTime code

1 vt_process_t *process_1; 2 vt_process_t *process_2; 3 vt_cpu_t *cpu;

4

5 void proc1( vt_process_t *me); 6 void proc2( vt_process_t *me); 7

8 int main() {

9 vt_init_simulation();

10 cpu = vt_create_cpu( "CPU" );

11 process_1 = vt_create_process( VT_PRI_PROC, "Process 1", 12 &proc1, VT_PRIORITY_MIN, cpu);

13 process_2 = vt_create_process( VT_PRI_PROC, "Process 2", 14 &proc2, VT_PRIORITY_MIN, cpu);

15 vt_run_simulation( 1000 ); 16 vt_exit_simulation(); 17 return 0;

18 } 19

20 void proc1( vt_process_t *me ) { 21 vt_signal_t sig;

22

23 while( true ) {

24 vt_receive( &sig );

25 vt_send( &sig, process_2 ); 26 vt_use_cycles( 500 );

(29)

3.1 Generating simulation models from OSE-application code 17

27 } 28 }

29

30 void proc2( vt_process_t *me ) { 31 vt_signal_t sig;

32

33 while( true ) {

34 vt_use_cycles( 100 ); 35 vt_send( &sig, process_1 ); 36 vt_receive( &sig );

37 } 38 }

This example shows the communication aspects of an OSE-application that can be modeled in VirtualTime. We can see how the OSE OS_PROCESS( process_name ) commands is the same as VirtualTime’s void process_name(vt_process_t *me). So the translation just needs to exchange the OSE version with the Virtual-Time version. The body can be left intact and relevant commands can be trans-lated later on. For example the OSE send(sig, process_name); is the same as vt_send(&sig, process_name). We can also see that delay, receive and send is almost identical to the vt_use_cycles, vt_receive and vt_send. We can also observe that a main function must be constructed and the VirtualTime processes must be initialized within the main function. There are also initial declarations in the beginning of the VirtualTime application that differ from the OSE-application.

An OSE-application can of course contain a lot more than communication between processes. But it is primarily the communication aspect that can be modeled in VirtualTime. The previous example does not show any of the complex dependencies and control flow that can be in an OSE-application. The following example shows an excerpt from an application with some tricky dependencies:

OSE Example

1 extern PROCESS some_process; 2 OS_PROCESS(Process) {

3 union SIGNAL sig; 4 int b = 500; 5 int c = 50; 6 int a = 1000 7 while( c < a ) { 8 send(sig, some_process); 9 delay(c); 10 c = c + b; 11 } 12 }

(30)

This example illustrates a more complex dependency that should be resolved and kept in the translation process as it has an affect on the communication statement. Wee see that delay(c); delays the application c timesteps. Now c is initialized to 50, but is then altered every iteration of the loop with c + b. This could make the translation more difficult.

3.1.1

Performance Requirements

In large projects with many lines of code you want the performance of the trans-lation to be steady and relatively fast. In this thesis there is an extra requirement that the time for the translation should not take longer than 100 times the compi-lation time of the OSE-application in GCC. The factor 100 is an arbitrary number chosen only to get some limit on the translation time.

3.2

Limitations

It should be understood that this is not an easy problem to solve with a com-puter. The translation can never be fully automatic. An OSE-application can be programmed in endless ways and it is close to impossible to cover all the cases in the translation. The translation is limited to translating the communication behaviour of the OSE-application.

3.3

Concise Problem Formulation

The key problem in the thesis is to translate OSE-application code to VirtualTime simulation model code. This translation needs to isolate the timing and commu-nication behavior of the OSE-application. The rest of the OSE-application does not need to be considered in the translation. The translation cannot be fully au-tomatic so the user will need to supply input, although as little input as possible, to help the translation.

(31)

Chapter 4

Straightforward Method

In this chapter the straightforward method will be explained in detail. The straightforward method was the first attempted solution to the problem defined in the problem definition chapter. First the algorithm behind the solution will be explained. Then an account of how the solution is implemented in TXL is pre-sented. A range of tests were used to evaluate the method and these are explained and the results are presented. The method does not work satisfactorily and an account of this is given in the last section of this chapter.

4.1

Algorithm

The approach we came up with was to translate the OSE-primitives that have equivalents in VirtualTime. There are some similarities of how the process bodies are declared in both OSE and VirtualTime. This could be used to preserve the process body structure and translate that into the VirtualTime equivalent. Un-fortunately, solving the problem this way still leaves unnecessary OSE-application code. See the following example:

OSE code

1 ...

2 OS_PROCESS(process_1) {

3 union Signal some_sig; 4 char *envvar;

5 while( true ) {

6 send(some_sig, process_2); 7 delay(500);

8 envvar = get_env( current_process(), "TERM"); 9 }

10 } 11 ...

Translated OSE code 19

(32)

1 ...

2 void process_1(vt_process_t *me) { 3 vt_signal_t some_sig; 4 char *envvar;

5 while( true ) {

6 vt_send(some_sig, process_2); 7 vt_use_cycles(500);

8 envvar = get_env( current_process(), "TERM"); 9 }

10 } 11 ...

Here you can see that line four and eight should be removed from the code to get a process that is completely implemented in VirtualTime concepts. To solve the problem we need to construct an algorithm that can both translate Virtual-Time equivalents and remove unnecessary code.

Another problem is that we have to construct the VirtualTime initialization code based on the existing OSE-code. This means we need to save the information needed to create the initialization code. The information that have to be saved are information about which processes that need to be initialized.

We have created an algorithm where we try to isolate the parts of the code that involves the timing and communication behavior of the OSE-application and then translate these parts. Necessary code needed for the initialization is saved to ease the construction. Then we remove unnecessary code and construct initialization code that is needed by VirtualTime. A main function is constructed using the saved process code. The algorithm is presented here:

1. Identify the different processes and for each process go through steps a to c.

(a) Within the scope of each process all the OSE command primitives that have VirtualTime equivalents are translated.

(b) Remove the unnecessary code that has not been translated and are not involved in the control flow.

(c) Signal declarations are translated to their VirtualTime equivalents.

2. Each translated process declaration from step one is saved for later use in temporary storage.

3. The process declarations that were saved in step two are put into an output buffer that is modifiable.

4. The main function is constructed using the saved process declarations from step two and added first in the prepared output buffer from step three.

(33)

4.2 TXL 21

5. The initial process declaration and other VirtualTime initialization is con-structed using the saved process declarations from step one and added first to the output buffer.

6. The output buffer is flushed and the algorithm is finished.

4.2

TXL

When implementing the algorithm seen in the previous section we used the lan-guage TXL, [7], which is a lanlan-guage constructed for source to source transforma-tions. In this section we will see how TXL mostly simplifies but also complicates the transformation process. Some of the implementation code will be explained and used as an example of how TXL has been used.

Translation of the commands is the most important task but also the easiest when implemented using TXL. Below is the code for translating the OSE send command:

TXL code

1 rule transformSend

2 replace [expression_statement] 3 send( & Id1 [id], Id2 [id] ); 4 by

5 vt_send( & Id1, ______CHANGEME______); 6 end rule

In this code snippet we see the typical pattern and replacement sections. The pattern is what is to be changed, in this case send( & Id1 [id], Id2 [id]);. The replacement replaces whatever matches the pattern and in this case the

re-placement is vt_send( & Id1, ______CHANGEME______);. The replace [expression_statement] describes which type of grammatical statement it is. Everything in TXL can be

thought of as grammatical trees, much like a parse tree. Every expression you replace must be of the same grammatical type as the one that is removed. As a TXL-rule works recursively this rule will be applied to every statement that is of the type [expression_statement]. This code snippet is a typical example of how a command is translated in TXL.

Below we see a more advanced example. This is the main function where the global variables are initialized and all functions are applied to the entire scope of the program:

Main function example

1 function main

2 export Table [repeat function_definition] 3 _

(34)

4 export SymbolTable [repeat declaration] 5 _

6 export CreateProcessTable [repeat expression_statement] 7 _

8 export ProcessTable [repeat function_definition] 9 _

10 export globalCPU [id] 11 cpu 12 replace [program] 13 All [program] 14 by 15 All 16 [buildSymbols] 17 [changeOS] 18 [replaceResult] 19 [addMainInfo] 20 [addDecls] 21 end function

First we see that all the global variables is initialized. Every global variable is initialized with _, which means an empty list, except globalCPU which is set with cpu. Then we create a pattern of the grammatical type [program] and we assign the whole input to the variable All. Then in the replacement we apply all the functions that transform the program. For example [buildSymbols] builds the simplistic symbol table based on the program. Adding the VirtualTime function to the translated code is done via [addMainInfo].

As we want to keep track of more complex dependencies we might want to use a symbol table to save all assignments. This is where TXL has its drawbacks. TXL is a fully functional language and assigning variables is not easy. There is built in support for global variables and you can export values and import values from these global variables. This is used in the example above. We see that we have the command export Table [repeat function_definition]. By exporting this we make it into a global variable. Other rules or function can access this global varibale by using the import-command. Global variables can be used to create a symbol table. The problem is that you need to create a symbol table for each grammatical type. This makes it difficult and complex to keep track of dependencies throughout different blocks of the program. TXL could be used as a postprocessing tool which only translates the relevant commands. Another tool could preprocess the code and remove the unnecessary parts and keep everything that is relevant for the code that is to be translated.

(35)

4.3 Testing and Results 23

4.3

Testing and Results

Testing was made to ensure that the application behaved as designed. The test cases consisted of OSE-applications with certain timing and communication behav-iors. The test cases also included unnecessary code that could be removed. They also contained both simple and complex dependencies. These test case programs were run through the translator to see if they produced the correct VirtualTime output. After some manual editing the test cases were run through VirtualTime to see if they worked. The test programs and the translated output can be seen in appendix B. We can also see some statistics from the tests in table 4.1. In the table we have information on how many single lines of code that was translated and how large the test programs were. The code that is not present in the input program but was constructed by TXL, e.g., the main function, is counted as translated code.

Nr Input program SLOC Outout Program SLOC Number of trans-lated SLOC 1 38 44 38 2 35 36 32 3 37 36 32 4 16 20 18 5 16 19 17

Table 4.1. This table shows the number of single lines of code, SLOC, in each test input

program and the translated output program. The number of translated single lines of code are also shown in the table.

The test programs were rather simple and did not contain many single lines of code. They do not really represent the large and complex OSE applications that are used in real time systems. But the test programs represent the level that the translator can handle. Below we see one of the test examples from appendix B. The example is from test three at page 55. In this example we can where the translation fail and the dependency is broken:

Test Input // Test file #include "ose.h" #include "stdio.h" #include "shell.h" #include "string.h" PROCESS proc1_; PROCESS proc2_;

static const SIGSELECT anysig[] = {0}; union SIGNAL {

(36)

};

OS_PROCESS(proc1) {

union SIGNAL *the_sig; union SIGNAL *the_sig2; proc1_ = current_process(); while(1) {

the_sig = alloc(0, 3); the_sig2 = alloc( 0, 2 ); send( &the_sig1, proc2_ ); send( &the_sig2, proc2_ ); the_sig = receive(any_sig); delay(1000);

} }

OS_PROCESS(proc2) { union SIGNAL *sig1; union SIGNAL *sig2; int d = 0;

proc2_ = current_process(); while(d < 1000000) {

sig1 = receive( any_sig ); delay( 500 );

sig2 = receive( any_sig ); free_buf( &sig1 );

free_buf( &sig2 ); d++;

} }

Here we see the output from the translator using the straightforward method:

Test Output #include <stdlib.h> #include <stdio.h> #include <vt_ose.h> vt_cpu_t *cpu; vt_process_t *proc1_;

void proc1 (vt_process_t *me); vt_process_t *proc2_;

void proc2 (vt_process_t *me); int main () {

vt_init_simulation (); cpu = vt_create_cpu ("CPU");

(37)

4.3 Testing and Results 25

proc1_ = vt_create_process (VT_PRI_PROC, "proc1", &proc1, VT_PRIORITY_MIN, cpu); proc2_ = vt_create_process (VT_PRI_PROC, "proc2", &proc2, VT_PRIORITY_MIN, cpu); vt_run_simulation (______INPUT_SIM_LENGTH_______);

vt_exit_simulation (); return 0;

}

void proc1 (vt_process_t *me) { vt_signal_t the_sig; vt_signal_t the_sig2; while (1) {

vt_send (&the_sig1, _________CHANGEME_________); vt_send (&the_sig2, _________CHANGEME_________); vt_receive (&the_sig);

vt_use_cycles (1000); }

}

void proc2 (vt_process_t *me) { vt_signal_t sig1; vt_signal_t sig2; while (d < 1000000) { vt_receive (&sig1); vt_use_cycles (500); vt_receive (&sig2); } }

We see here that all statements with d is removed from the VirtualTime code. But d is used as a condition in the while loop and without the definition and incrementaion of d the simulation will not work. This is of course easy to fic in this example. With larger applications this will get more complex and more dif-ficult as there are more dependencies which can be spread out throughout the code.

The testing thus revealed that complex dependencies could not be tracked. It also revealed that selecting what to remove and what to keep in the code is needed to get a working translation. The conclusion is that with this method the

(38)

translation will only work on simple OSE-applications that do not contain complex dependencies and only the communication and timing behavior can be translated.

4.4

Why does it not work?

The main reason why the straightforward method does not work is that it becomes close to impossible to know which code to remove and which to keep. This is because the dependencies between variables cannot be broken in the translation. This method is too crude and removes code which might be needed later for control flow or command parameters. All dependencies between all variables and statements need to be considered so that we can remove code based on that. An attempt to solve these problems are described in the next chapter.

(39)

Chapter 5

Improved Method

This chapter will describe the improved method which has more sophisticated ways of selecting which code to remove and which to keep in the translation process. By using data flow analysis a system dependence graph can be constructed. The system dependence graph is used to slice away parts of the OSE-application that is not needed for the translation. Backward slicing in general will be explained. The importance of a system dependence graph in backward slicing will be discussed. Backward slicing might be done in TXL and this will also be discussed. We will go through an algorithm for the improved method. There are limitation to the improved method and these will be discussed.

It is important to point out that backward slicing is still very much a research field. There are not very many tool implementations out there that provides back-ward slicing on large and complex languages like ANSI C. There exists no example, that we know, of a backward slicing TXL-implementation on ANSI C.

The reader should observe that an implementation using this improved method was not implemented so we do not know if it is even possible to implement, or how successful the method is.

5.1

Backward Slicing

Program slicing was first introduced by Mark Weiser in [18]. Program slicing could be used in debugging to find errors more easily. A slice, more precisely a backward slice, of a program consists of, with respect to a point p and a vari-able x, all statements and predicates that might affect the value of x at point p, as defined by Horwitz, Reps and Binkley in [10]. This means the we can slice our OSE-application with respect to our translatable OSE-commands and get a program that would only contain the necessary code for the communication and timing behavior. An example of how a sliced program could look like is seen in figure 5.1. Backward slicing can be implemented using a system dependence graph

(40)

which can handle multiprocess programs, and the OSE-applications are multipro-cess programs.

Figure 5.1. Here we see an example of a backward slice. The left program is sliced with

respect to the point return(d) and the variable d.

In figure 5.1 the left application represents an unsliced application and the right application is sliced with respect to the statement return(d); and the variable d. We see that in the right application the line int d = 5; and c=c+i; are removed because they don’t affect the value of d at the statement return(d);.

5.1.1

System Dependence Graph

The system dependence graph is an extension of the program dependence graph, see [13]. By setting up a system dependence graph we can track the dependen-cies within and between procedures. The system dependence graph is calculated by creating procedure dependence graphs for each procedure. A procedure de-pendence graph is just like a program dede-pendence graph but for an individual procedure. This calculation is detailed in [10] but for the interested reader a pro-gram dependence graph consists of vertices for each statement in the propro-gram and edges between them that shows the dependences between the statements.

There exists several types of edges between the vertices. The edges are control dependence, def-order dependence, loop-independent flow and loop-carried flow. The edges are computed using data flow analysis. Our example, based on an ex-ample in [10], of a program dependence graph is shown in figure 5.2.

(41)

5.1 Backward Slicing 29

The example is written in a syntax very similar to C but we assume that the language only contains assignments, conditionals and while statements. Then we can see in figure 5.2 how the different edges works. The control edge, for an easy language as this, reflects the nesting structure of a program. The control depen-dence edge is between each entry vertex and every statement that is not nested within a loop. The control dependence edge is also between each predicate that controls a loop or conditional and each statement inside the loop or conditional. If the statement is inside a conditional the edge between the predicate and the statement is labeled true or false depending on whether the statement occurs in the then branch or the else branch.

The loop-independent flow dependence edge is between a vertex that defines a variable and a vertex that uses the variable. The loop-carried flow dependence edge is between two vertices that is enclosed in a loop and where one vertex define a variable that the other vertex uses. The loop-carried flow dependence would dis-appear if the loop predicate was removed but the-loop independent flow dependece would not. The def-order dependence edge has to do with the definition order of a variable. The descriptions of the edges is not very detailed and does not have the exact definitions that we see in [10]. There we can see a detailed definition on each edge and how they can be calculated. Hopefully, with this example, an intuitive understanding of backward slicing and a program dependence graph has been attained.

The program dependence graph works for simple examples like the one de-scribed in figure 5.1. But to slice interprocedural programs we need to extend the program dependence graph into an system dependence graph, see [10]. Each procedure is represented by its own program dependence graph that is called a procedure dependence graph. The procedure dependence graph is different from the program dependence graph in the way that it does not have an end ver-tex. Instead it has a new type of vertex that is the call verver-tex. The call vertex represents a procedure call. The procedure dependence graphs are connected by call edges and parameter in and out edges. Each parameter in a procedure call is represented in the system dependence graph as an assignment vertex where a temporary variable is assigned the parameter. The parameters between the procedure dependence graphs are connected by parameter edges. The procedure dependence graphs is also connected by a call edge between the call vertex from the calling procedure to the entry vertex in the called procedure. In [10] they also introduce a linkage grammar to get more exact slices but this is not explained here.

To be able to slice an application we can again use the program dependence graph in figure 5.2 as an example of how it is done with a simpler language. If we assume that we have the program that is in figure 5.1 and the the program dependence graph mentioned earlier and that we want to slice the application with respect to statement return(d); and the variable d. Then we can follow the loop-independent dependence edges and the loop-carried dependence dependence

(42)

Figure 5.2. Here we see a program dependence graph, based on the program in figure

5.1.

edges that point to the vertex return(d) to the vertex that it starts from. These vertices will be left intact in the program and will not be sliced away. Then we do the same procedure from these vertices and we follow loop-independent and loop-carried dependence edges that points to the vertices and save the vertices that they starts from. We do this until there is no more vertices to go to and we throw away the vertices that we have not visited in the process. This way we get the sliced program in the right of figure 5.1.

5.1.2

Backward Slicing with TXL

The straightforward method used TXL to translate the OSE-application into a VirtualTime model. TXL is very good for translating but is hard to use when tracking dependencies. Backward slicing combined with the translation power of TXL could be an adequate solution to the problem of tracking dependencies and then translating the OSE-commands.

We found no known examples of a backward slicer for C in TXL but there is an example of a backward slicer for TIL, Tiny Imperative Language, written by James R. Cordy in [8]. TIL is a very simple language used for educational purposes but the example shows that there is a possibility that it could be done

(43)

5.2 Algorithm 31

for a larger language like C. But the issue of aliasing, pointers and interprocess programs could make it too complex to be done.

The principle of the TIL example is that you mark the statement that the program should be sliced from with a XML markup tag. Then TXL checks each unmarked statement to see if it has any affect on any of the marked statements. Each statement that affects a marked statement get marked itself. When all statements have been checked then all unmarked statements is removed and finally the markup is removed and the sliced program is left.

5.2

Algorithm

We will here propose an algorithm that uses a combination of backward slicing and parts of the existing straightforward method to get a better solution that tracks and keeps important dependencies and translates the relevant OSE-commands. This algorithm could be divided into two phases. The different phases could be done in separate programs to be able to exploit characteristics of different pro-grams.

The two different phases are first backward slicing that preprocesses the code for the second phase that is a translation phase. The translation phase has already been done in the straightforward method and only small modifications would be needed. Therefore TXL would be well suited for the second phase.

The first phase could be implemented in a number of ways. If we could get a working backward slicer in TXL then this would be the way to go as the second phase is also best written in TXL. There is a known commercial tool, CodeSurfer [1], that implements a backward slicer for C but this has not been tested by us. This tool is based on the Wisconsin Program-Slicing tool, [4]. This could be used to explore the potential of the slicing method and it could possibly be used for the first phase of the translation.

An outline algorithm for slicing an OSE-application could work as follows: 1. Construct a system dependence graph Gs from POSE

2. Each important OSE-command that can be translated into VirtualTime equivalents and the parameters to that command is saved as a point pi...pk

and the variables x. Point pi = ... = pk but the variables are not the same.

This is needed if there are multiple parameters in a command that we want to slice with respect to all parameters.

3. Program POSE is sliced into PSlices using system dependence graph Gs,

taken with respect to all points p and variables x from POSE that was saved

in step two.

(44)

The transformation function T (P ) is the transformation of the OSE-commands.

POSE is the OSE-application and PV T is the VirtualTime model. This algorithm

describes the two phases that we talked about earlier in the text. The algorithm should be seen as an idea of how the improved method could be implemented. The time complexity of the algorithm is with the exception of the construction of the system dependence graph linear with respect to the program size according to [10]. This algorithm has not been designed with optimality as a factor and it might need to be optimized in the future.

(45)

Chapter 6

Discussion

Here we will discuss how the work in the thesis went. We will also go through the tools used in the thesis. General issues in the thesis will be discussed. We will also go through related work.

6.1

General Discussion

The work started with a feasibility study and requirements on the solution were identified. Then the straightforward method was constructed and tested. More than halfway in the thesis we came to the conclusion that the straightforward method was not powerful enough to solve the problem. New solutions were re-searched and after much studying the improved method was formulated. Unfor-tunately there was not enough time to implement and test this method.

The straightforward method did not work because it was not sophisticated enough to spot dependencies between OSE-code that is to be translated and other code that the OSE-code depend on. The method does not know which code to keep and which to throw away and that makes the translation incomplete. A method that keeps track of the dependencies in the code is needed and the im-proved method uses a technique called backward slicing. Backward slicing removes parts of the code that is not related to specified statements and variables in the code.

The improved method deals with the difficulties from the straightforward method but backward slicing, the technique used in the improved method, is not done ef-fortlessly. Backward slicing is relatively well documented and the principles and theory is accessible and not too hard to learn. But it has not been used too much with C and it is not easy to come by applications that uses C slicing. This might complicate the implementation of backward slicing. TXL and backward slicing might be an approach for implementing the improved method but this might not be easy. In discussions with James R. Cordy, one of the developers of TXL, he believed it might be a bit difficult as C has pointers and interprocess

(46)

tion. If we were asked how long it would take to implement the improved method we would answer that it would take at least as much work as the work done until this point. A lot of studying and prototyping would be needed and decisions of which tools that should be used would take up time. So an approximation is that the work of implementing the improved method would take about 20 weeks. This includes writing all the documentation needed. But the time should be seen as an approximation and if the improeved doesn not work as planned then the work could take even more time.

6.2

Discarded Techniques and Methods

Other related work and techniques were studied in the beginning of the thesis work. Techniques and solutions were discarded. Island grammars, [17], were considered as it would be able to isolate the necessary OSE-code for translation. Island gram-mars are about special gramgram-mars that are produced to be able to parse languages within languages. In our case we are just interested in the communication and timing behavior of the OSE-application so the ideas was that we could write an island grammar for that. But using island grammars needed a special parser that were able to handle that kind of grammar. We tried using island grammars with both ANTLR, [14], and TXL, [7]. We investigated JavaCC, see [2], but it was rejected at the time as it was quite similar to ANTLR. None of these could be used with island grammars in the way we wanted them. Instead TXL could be used to filter out the unnecessary code and the straightforward method was based on this. But instead of an island grammar we used an existing C-grammar.

6.3

Related Work

A lot of articles were studied in this thesis and we will go through some of the related work here. It was quite difficult to find any work on the same subject. There is of course a lot of work that has been done with source to source transfor-mations but most of this has been for language transfortransfor-mations like Pascal to C. We looked for cases where only a subset of the language was important to translate but material on this was scarce. We have found some examples of related work using TXL and some using some kind of slicing technique.

An example where TXL is used is in [5] where schemas are translated between a Relational model, hierarchical model and an Entity Relationship model. They have solved this by being able to translate from the Relational model to the ER-model and back and likewise between the hierarchical ER-model and the ER-ER-model. If they want to translate from the hierarchical model to the Relational model they translate from the hierarchical model to the model and then from the ER-model to the Relational ER-model. They have implemented this using TXL.

(47)

6.4 Future Work 35

Another interesting example that is somewhat similar to this work is [6]. There they want to analyze Java Remote Method Invocation, Java RMI, in VeriSoft but VeriSoft can only analyze C++ applications. Therefore they translate from Java RMI to a C++ equivalent and then they run this program through VeriSoft. They do this by first translating Java semantics into C++ equivalents. Then they translate that into Verisoft Stubs so that the translated Java RMI program can be analyzed by VeriSoft. The translation tasks is done in TXL.

In [12] a technique that supports the re-engineering of parallel programs based on point-to-point communication primitives is explored. Pattern detection is used to find process interactions and classify them into a set of commonly ocurring interaction patterns. This could be used to transform parallel programs that uses point to point communication into collective communication programs.

6.4

Future Work

As the resulting application from this work used the straightforward method the future work should be based on the improved method. There is still a lot of ques-tion marks left in the improved method. Can you do backward slicing for C with TXL? How well does the method work? Can the communication between processes be tracked? These issues need to be investigated more before an implementation using the improved method is created.

Thought should be put into solving the issue of communication between pro-cesses. There needs to be a way to resolve to whom a process is communicating with. There are analysis techniques for inter-process communication between par-allel programs, [12], but they are not similar to the conditions in OSE. The con-ditions for tracking inter-process communication in OSE could be investigated in the future work so that an estimation of how difficult the issue is could be obtained.

A difficult decision is which tool to use for the improved method. TXL works fine for the translation of the OSE-commands but might not be suited for tracking the dependencies in the application. Therefore it should be investigated if another tool besides TXL could be used, or if another tool could be used in conjunction with TXL. One possibility could be to create a new tool handling both the translation and the backward slicing, but this would probably be a lot of work.

(48)
(49)

Bibliography

[1] Grammatach: Codesurfer. http://www.grammatech.com/products/codesurfer/overview.html, Last checked: November 2007. Web page about the backward slicing tool

CodeSurfer.

[2] Javacc web page. https://javacc.dev.java.net/, Last checked: December 2007. Resources and documents about JavaCC can be found here.

[3] Txl web page. http://www.txl.ca, Last checked: November 2007. Resources and documents about TXL can be found here.

[4] Wisconsin program slicing tool. http://www.cs.wisc.edu/wpis/html/, Last checked: November 2007.

[5] Rateb Abu-Hamdeh, James R. Cordy, and Patrick Martin. Schema trans-lation using structural transformation. In CASCON ’94: Proceedings of the

1994 conference of the Centre for Advanced Studies on Collaborative research,

page 1. IBM Press, 1994.

[6] Tim Cassidy, Tom Dean, Jim Cordy, and Juergen Dingel. Concur-rency analysis of java rmi using source transformation and verisoft. cite-seer.ist.psu.edu/671603.html.

[7] James R. Cordy. The txl source transformation language. In Science of

Computer Programming, volume 61, pages 190–210, august 2006.

[8] James R. Cordy. Til-example. http://www.program-transformation.org/Sts/BackwardSlicingUsingTXL, Last checked: November 2007. An example of backward slicing on TIL using TXL.

[9] Enea. OSE 4.3 Documentation Volume 1 - Kernel.

[10] Susan Horwitz, Thomas Reps, and David Binkley. Interprocedural slicing using dependence graphs. SIGPLAN Not., 39(4):229–243, 2004.

[11] Russel Halliday James R. Cordy, Ian H. Carmichael. The TXL Programming

Language - Version 10.4, January 2005.

References

Related documents

Cyklus repeat se od cyklu while liší zejména tím, že podmínka je až na konci a tudíž cyklus repeat proběhne za všech okolností minimálně jednou.. Druhý rozdíl je,

Detta skul- le för denna undersökning innebära att man först vore tvungen att göra en (im- plicit eller explicit) prognos över im- porten för att erhålla en konsumtions-.. Danmark

Sökväg: UBW Planering &gt; Systeminställning &gt; Import av transaktioner &gt; Browsermallänk samt Från browser till transaktionstabell – PL205-Importera kapitalkostnader från

 Välj RefWorks Direct Export I rullgardinsmenyn Export format samt klicka på Export och referenserna exporteras direkt till RefWorks. Web

The edges contain information about invocation instructions. We refer to edges corresponding to such instructions as visible, and label them with a method sig- nature.

We have, based on experiences from real parallel programs, identified three simulation mod- els, called Direct Model, Client-Server Model, and Strict Sequence Model.. Measurements on

[r]

I fältet Temporär geodatabas för utdata, ange sökväg till var den temporära utdatabasen ska lagras genom att klicka på mappikonen till höger om fältet.. I fältet