System Programming Lab Manual
System Programming Lab Manual
Lab Manual
1 Design of macro-processor 3
2 8
Implementation of Nested Macros
3 Design a two-pass assembler with 10
respect to hypothetical instruction set.
6 27
Simulation Of Linker.
7 Simulation Of Loader 30
9 36
Writing and using a Dynamic Linking
Library (DLL)
10 49
Use of different debugger tools.
ASSIGNMENT NO-1
Pre Lab-
Theory-
An assembly language macro facility is to extend the set of operations provided in an assembly
language.
In order that programmers can repeat identical parts of their program, macro facility can be used.
This permits the programmer to define an abbreviation for a part of program & use this
abbreviation in the program. This abbreviation is treated as macro definition & saved by the
macro processor. For all occurrences the abbreviation i.e. macro call, macro processor substitutes
the definition.
It consists of
1. Macro Prototype Statement - This declares the name of macro & types of parameters
like positional, keyword or mixing of both.
2. Model statement - It is statement from which assembly language statement is
generated during macro expansion.
3. Preprocessor Statement - It is used to perform auxiliary function during macro
expansion.
Macro Call & Expansion:
The operation defined by a macro can be used by writing a macro name in the mnemonic field
and its operand in the operand field. Appearance of the macro name in the mnemonic field leads
to a macro call. Macro call replaces such statements by sequence of statement comprising the
macro. This is known as macro expansion.
4. Counters MNTC, ALAC, and MDTC for MNT, ALA and MDT respectively.
Sample Input -
The assembly language program with macro definitions & macro calls
MACRO
MEND
START
MOVER AREG, B
ADD AREG, N
INCR A, B, AREG
MOVEM AREG, N
MOVEM BREG, N
MOVEM A, N
A DS 5
B DS 1
N DS 1
END
Sample output -
Macro Name Table Argument List Array table
2 &INCR_VAL B
3 ® AREG
1 MOVER #3,#1
2 ADD #3,#2
3 MOVEM #3,#1
4 MEND
Expanded code with no macro definition & macro calls
START
MOVER AREG, B
ADD AREG, N
MOVER AREG, A
ADD AREG, B
MOVEM AREG, A
MOVEM AREG, N
MOVEM BREG, N
MOVEM A, N
A DS 5
B DS 1
N DS 1
END
Conclusion-
With the help of implementation of this two pass macro processor we demonstrated that the
macros are used to provide a program generation facility through macro expansion which is
lexical expansion or semantic expansion.
Advancement-
The program can be extended for
Post Lab-
Following objectives are met
To understand macro facility, features and its use in assembly language programming.
To study how the macro definition is processed and how macro call results in the
expansion of code.
Viva Questions-
1. Define the term macro.
2. Distinguish between macro and a subroutine
3. Define and Distinguish between parameters that can be used in macros.
4. State various tables used in processing the macro.
5. Explain the role of stack in nested macros.
ASSIGNMENT NO-2
Nested macro calls refer to the macro calls within the macros.
•A macro is available within the other macro definitions
.•In the scenario where a macro call occurs, which contains another macro call, the macro processor generates the
nested macro definitions text and places it on the input stack.
•The definition of the macro is then scanned and the macro process or compiles it.
•The following example can make you understand the nested macrocall.
MACRO
SUB 1 &PAR
L 1,&PAR
A 1,=F’2’
ST1,&PAR
MEND
MACRO
SUBST &PAR1, &PAR2,&PAR3
SUB1&PAR1
SUB2&PAR2
SUB3&PAR3
MEND
It can be easily noticed from the example that the definition of the macro ‘SUBST’ contains
three separate calls to a previously defined macro ‘SUB1’.
•The definition of the macro ‘SUB1’ has shortened the length of the definition of the macro
‘SUBST’.
Instruction set should include all types of assembly language statements such as Imperative,
Declarative and Assembler Directive. While designing stress should be given on
A) How efficiently Mnemonic opcode table could be implemented so as to enable faster retrieval
on op-code.
OBJECTIVES:
Theory-
Assembler: It creates object code by translating assembly instruction mnemonics into opcodes,
and by resolving symbolic names for memory locations and other entities.
There are two types of assemblers based on how many passes through the source are needed to
produce the executable program.
One-pass assemblers go through the source code once and assume that all symbols will
be defined before any instruction that references them.
Two-pass assemblers create a table with all symbols and their values in the first pass, and
then use the table in a second pass to generate code. The assembler must at least be able to
determine the length of each instruction on the first pass so that the addresses of symbols can
be calculated.
Pass I =>
As shown in fig.1.1 the Pass I perform analysis of the source program and synthesis of the
intermediate representation while Pass II processes the intermediate representation to synthesize
the target program.
Data Structures
1) Opcode Table: Assembler need not to enter information in this table. This table with
information is available with the assembler.
The assembler does not enter information in this table but uses this data structure
a) Opcode
b) Machine code
c) Type of Opcode
d) Length of Opcode
2) Register Table: This table stores the information about registers supported by system.
Fields of Register Table:
a) Register name
b) Machine constant
3) Symbol Table: This table is used to record information about symbols in program.
Symbols can be
a) Symbol no
b) Symbol name
c) Symbol address
d) Symbol length
4) Literal Table: This table is used to store the information about literals. This table is created
by Pass-I and used by Pass-II of 2-pass assembler.
Fields of Literal Table:
a) Literal no
b) Literal
c) Literal address
5) Literal Pool Table: This table stores information about literal pools in the assembly program.
No of literal pools is always 1 greater than number of LTORG statements in assembly
program.
This table is created by Pass-I and used by Pass-II of 2-pass assembler.
Pool table can be implemented as one dimensional array where index of array indicates literal
pool no and POOLTABLE[x] indicates literal number of starting literal in literal pool ‘x’
1) Address:
Address can be specified or determined by using location counter
2) Opcode:
It has two parts:
Opcode Operand
1) Opcode:
It consists of Machine code of the opcode
2) Operand:
It has two parts:
a) First Operand i.e. Register operand with machine code (Machine constant)
b) Second Operand i.e
Symbol operand with machine code as its address or
Instruction set
2. Registers used: - This machine uses only four registers AREG, BREG, CREG and DREG.
Every register has an address as shown in Register Address Table (RAT).
Register Address
AREG 000
BREG 001
CREG 010
DREG 011
Register Address Table
STOP 00 01 1
ADD 00 02 3
SUB 00 03 3
MULT 00 04 3
MOVER 00 05 3
MOVEM 00 06 3
DIV 00 07 3
READ 00 08 2
PRINT 00 09 2
DC 01 00 ----
DS 01 01 ----
START 10 01 ---
END 10 02 ---
EQU 10 03 ----
Symbol Address
LOOP 45
….. …..
=’5’ 67
….. …..
START 101
PRINT SUM
ONE DC ‘2’
TWO DC ‘3’
6. Format of OUTPUT file (Object Module): - Assembler has to generate a file,
which need to be linked and loaded for execution. Such an output file is called as Object
file or object module. In this file format relocation table and Linking information is
stored; but for simplicity we will consider format of out put file as shown below.
Size of program
Object code
ALGORITHM 1 (PASS 1)
1. Set LC=0.
2. While next statement is not an END then
a). If symbol definition is found then make an entry in ST.
b). If START then LC= Value given after start. Generate intermediate code
c). If EQU statement then add symbol to ST which is on RHS of EQU and
d) If declaration statement then find size of memory area required and code
1. LC=LC + length
2. Generate intermediate code (00, CODE)
3. If operand is Literal then enter it into LT. IF IT is symbol then enter it into
ST.
4. If END statement generate intermediate code (10, 02) and goto pass-II.
OUT PUT of PASS-I
ALGORITHM 2 (PASS 2)
1. Define a code area ( to some max value of program size), code buffer
(to max size of any instruction) and LC=0 and variable SIZE=0;
address (code-area-address)+ LC
ii) LC=LC+SIZE
TEST CONDITIONS.
1. Give input as the sample source code and check the outputs of pass 1 and pass 2.
2. Cross check the values of LC.
3. Try to write another source code for this hypothetical instruction set and cross
check out put of pass 1 and pass 2.
APPLICATIONS.
2. Single pass assembler is used when efficient assembling is required and multi
FAQs.
Compilers that produce an executable (or the representation of an executable in object module format)
as opposed to a program in an intermediate language (and, in fact, for optimization purposes, all
compilers) need to make use of a symbol table .
The symbol table records information about the identifiers in the source program such as their name,
type, no. of dimensions, space assignment, etc.
#include<stdio.h>
void main()
int a,b,c;
char d;
float f;
c=a+b;
printf(“%d”,c)
label:
printf(“%c”,d)
end:
This file is given as an input to our program for generating symbol table.
Our Program should filter all the variables and the labels and store their symbol size and address.
Algorithm
3.literal should be added with the symbol size and address of the literal.
4.while finding literals consider only variables and labels ,ignore all the keywords and reserved words in
‘c’.
a 2 0
b 2 2
c 2 4
d 1 6
f 4 7
label
end
ASSIGNMENT NO-5
Title-Screen Editor
Pre Lab-
Student should be familiar with computer graphics concepts and its implementation in C.
Theory-
Editors are program entry and editing tool through which programs are entered into the
computer. It allows creation, update and deletion of items in a file.
A typical editor’s GUI has a menu bar, a set of buttons on various toolbars, other controls and
finally a large “canvas” area for display and direct manipulation of the document.
1) Line editors
2) Stream editors
3) Screen editors
4) Word processors
5) Structure editors
Line editor is a text editor computer program that is oriented around lines. The line editor
supports editing operations where basic unit is a line.
Structure editors can be used to edit hierarchical or marked up text, computer programs,
diagrams, chemical formulas, and any other type of content with clear and well-defined structure.
Structure editing predominates when content is graphical and textual representations are
awkward.
Design of an Editor-
The fundamental functions in editing are traveling, editing, viewing and display.
Viewing implies formatting the text in a manner desired by user. This is an abstract view,
independent of the physical characteristics of an IO device.
Display component maps this view into physical characteristics of display device being used.
Command
Processor
Editing Viewing
Buffer Buffer
Editing Viewing
Filter Filter
Text/Display
Figure 7.1
Figure 7.1 illustrates the schematic of a simple editor. For a given position of the editing context,
the editing and viewing filters operate on the internal form of text to prepare the forms suitable
for editing and viewing. These forms are put in the editing and viewing buffers respectively. The
viewing-and-display manager makes provision for appropriate display of this text. When the
cursor position changes; the filters operate on a new portion of text to update the contents of the
buffers. As editing is performed, the editing filter reflects the changes into the internal form and
updates the contents of the viewing buffer.
ALGORITHM
1. Use an array of character that can store a line full of text (of size 80 chars)
2. Ask the user which file to newly create or open an existing file.
3. After opening a file editor must allow user for appending a new line at the end of
opened file, allow him to delete a specified word, allow adding a given word after a
specified word of a line, and allow reading an entire line by line. Program has to
extensively use array of characters.
4. When editor program is executed it must provide following alternatives to user
1. Open an existing file
2. Open anew file
3. Opening a file in appending mode
4. Deleting a file
TEST CONDITIONS.
APPLICATIONS.
Conclusion
The following features of screen editor are studied with the help of this implementation
1. Text is conceptually split into screen
2. The user has full control over entire terminal where screen is displayed
3. The user can move cursor over screen for editing operations
4. The user can bring delete a character by pressing delete or backspace key
Advancement-
Post Lab-
Viva Questions-
THEORY:
Computer programs typically comprise several parts or modules; all these parts/modules
need not be contained within a single object file, and in such case refer to each other by means of
symbols. Typically, an object file can contain three kinds of symbols:
When a program comprises multiple object files, the linker combines these files into a unified
executable program, resolving the symbols as it goes along.
Linkers can take objects from a collection called a library. Some linkers do not include the
whole library in the output; they only include its symbols that are referenced from other object
files or libraries. Libraries exist for diverse purposes, and one or more system libraries are
usually linked in by default. This may involve relocating code that assumes a specific base
address.
The linker also takes care of arranging the objects in a program's address spaceto another
base. Since a compiler seldom knows where an object will reside, it often assumes a fixed base
location (for example, zero). Relocating machine code may involve re-targeting of absolute
jumps, loads and stores.
The executable output by the linker may need another relocation pass when it is finally
loaded into memory (just before execution). This pass is usually omitted on hardware offering
virtual memory — every program is put into its own address space, so there is no conflict even if
all programs load at the same base address. This pass may also be omitted if the executable is a
position independent executable.
ALGORITHM:
1. What is Linker?
2. How Linker performs Linking?
3. What kind of files are accepted by Linker for Linking?
4. Is there any difference between Linker and Relocator?if yes. Explain.
5. The executable output by the linker may need another relocation pass. Why?
Experiment Number: 07
THEORY:
In computing, a loader is the part of an operating system that is responsible for loading
programs. It is one of the essential stages in the process of starting a program, as it places
programs into memory and prepares them for execution. Loading a program involves reading the
contents of executable file, the file containing the program text, into memory, and then carrying
out other required preparatory tasks to prepare the executable for running. Once loading is
complete, the operating system starts the program by passing control to the loaded program code.
All operating systems that support program loading have loaders, apart from systems where code
executes directly from ROM or in the case of highly specialized computer systems that only have
a fixed set of specialised programs.
In many operating systems the loader is permanently resident in memory, although some
operating systems that support virtual memory may allow the loader to be located in a region of
memory that is pageable.
In the case of operating systems that support virtual memory, the loader may not actually copy
the contents of executable files into memory, but rather may simply declare to the virtual
memory subsystem that there is a mapping between a region of memory allocated to contain the
running program's code and the contents of the associated executable file. (See memory-mapped
file.) The virtual memory subsystem is then made aware that pages with that region of memory
need to be filled on demand if and when program execution actually hits those areas of unfilled
memory. This may mean parts of a program's code are not actually copied into memory until
they are actually used, and unused code may never be loaded into memory at all.
In Unix, the loader is the handler for the system call execve().The Unix loader's tasks include:
ALGORITHM:
1. What is Loader?
2. How Loader works?
3. What kind of files are accepted by Loader?
4. Is there any difference between Linker and Loader?if yes. Explain.
5. List out the Functions of Loader?
Experiment Number: 08
TITLE: Design Lex specifications for the tokens – keywords, identifiers, numbers,
operators, white spaces.
OBJECTIVES:
THEORY:
This is a simple tutorial that tells how to use FLEX (Fast LEXical analyzer), which is a LEX tool
supplied with Linux as a free software.
1. A first program sample1.flex is as follows that recognizes only two keywords viz. begin
and end (remember that lex programs are case sensitive ) and file extension .flex is
optional but it is recommended to use to remember which file is associated with LEX.
%{
#include<stdio.h>
%}
%%
%%
How to execute this file ? Execute the following commands at command promt
1. $ flex sample1.flex
- Execution of this command will generate a file lex.yy.c in your present working
directory that can be checked
- By using $ ls command.
2. $ gcc lex.yy.c –l f l
- gcc is GNU compiler collection used to compile lex.yy.c file and the option –lfl
adds linking libraries to the
- compiled file a.out
- For executing file a.out use next step
3. $ ./a.out
- program will wait for you to type and when keywords begin and end are typed
then program prints a message that keyword is found
- A sample output session with this program is given below. The session can be
stopped with ctrl-D.
begin
end
END
BEGIN
BEGIN
i am happy
2. Second program is sample2.flex. It takes into account keywords, identifiers and
punctuation symbols.
%{
#include<stdio.h>
%}
%%
if printf("keyword if found");
float printf("keyword");
; printf("SEMICOLON ");
%%
3. Write a program which include all the keyword of C language which are given below
along with some punctual symbols of C.
auto double int struct
do if static while
ALGORITHM
1. Write a LEX program using any text editing tool over Linux give the extension .flex
to it.
2. Compile it using command $ flex abc.. flex
Execution of this command will generate a file lex.yy.c in your present working
directory that can be checked by using $ ls command.
3. Compile it for C using $ gcc lex. yy. c –l f l . We will get a. out as output and
executable file.
4. To execute this program use $ ./a.out
5. Then type some tokens and non tokens and check the response of program.
APPLICATIONS.
OBJECTIVES:
PRE REQUISITE: - Theoretical knowledge of DLL and dynamic linking and loading.
THEORY:
The idea of a plugin is familiar to users of Internet browsers. Plugins are downloaded from the
Web and typically provide enhanced support for audio, video, and special effects within
browsers. In general, plugins provide new functions to an existing application without altering
the original application.
DLLs are program functions that are known to an application when the application is designed
and built. The application's main program is designed with a framework or backplane that
optionally loads the required dlls at runtime where the dlls are in files separate from the main
application on the disk. This packaging and dynamic loading provides a flexible upgrade,
maintenance, and licensing strategy.
Linux ships with thousands of commands and applications that all require at least the libc library
functions. If the libc functions were packaged with all programs, thousands of copies of the same
functions would be on the disk. Rather than waste the disk space, Linux builds these applications
to use a single system-wide copy of the commonly required system libraries. Linux goes even
further. Each process that requires a common system library function uses a single system-wide
copy that is loaded once into memory and shared.
In Linux, plugins and dlls are implemented as dynamic libraries. The remainder of this article is
an example of using dynamic libraries to change an application after the application is running.
Applications in Linux are linked to an external function in one of two ways: either statically
linked at build time, with static libraries (lib*.a) and having the library code include in the
application's executable file, or dynamically linked at runtime with shared libraries (lib*.so).
The dynamic libraries are mapped into the application execution memory by the dynamic linking
loader. Before the application is started, the dynamic linking loader maps the required shared
object libraries into the application's memory or uses system shared objects and resolves the
required external references for the application. Now the application is ready to run.
As an example, here is a small program that demonstrates the default use of dynamic libraries in
Linux:
main()
{
printf("Hello world
");
}
When compiled with gcc hello.c, an executable file named a.out is created. Using the Linux
command ldd a.out, which prints shared library dependencies, the required shared libraries
are:
The same dynamic linking loader is used to map a dll into the application's memory after it is
running. The application controls which dynamic libraries are loaded and which functions in the
libraries are called by using Linux dynamic loader routines to perform the loading and linking
and to return the addresses of the required entry points.
dlopen opens and maps into memory the shared objects file and returns a handle
dlsym return a pointer to the requested entry point
dlerror returns either NULL or a pointer to an ASCII string describing the most recent
error
dlclose closes the handle and unmaps the shared objects
The dynamic linking loader routine dlopen needs to find the shared object file in the filesystem
to open the file and create the handle. There are four ways to specify the file's location:
The dynamic linking loader example program is a small C program designed to exercise the dl
routines. Based on the one C program everyone has written, it prints "Hello World" to the
console. The original message printed is "HeLlO WoRlD". The test links to two functions that
print the message again: the first time in all uppercase characters and then again in lowercase
characters.
1. The dll include file dlfcn.h and required variables are defined. The variables needed, at
a minimum, are:
o Handle to the shared library file
o Pointer to the mapped function entry point
o Pointer to error strings
3. The shared object file for the UPPERCASE dll is opened by dlopen and the handle is
returned using the absolute path "/home/dlTest/UPPERCASE.so" and the option
RTLD_LAZY.
o Option RTLD_LAZY postpones resolving the dll's external reference until the dll
is executed.
o Option RTLD_NOW resolves all dll external references before dlopen returns.
4. The entry point printUPPERCASE address is returned by dlsym.
6. The handle to UPPERCASE.so is closed by dlclose, and the dll is unmapped from
memory.
7. The shared object file, lowercase.so, for the lowercase dll is opened by dlopen and the
handle returned using a relative path based on the environmental variable
LD_LIBRARY_PATH to search for the shared object.
10. The handle to lowercase.so is closed by dlclose and the dll is unmapped from memory.
Note that after each call to dlopen, dlsym, or dlclose, a call is made to dlerror to get the last
error and the error string is printed. Here is a test run of dlTest:
The complete source lists for dlTest.c, UPPERCASE.c and lowercase.c are under Listings later
in this article.
Building dlTest
#!/bin/ksh
# Build shared library
#
#set -x
clear
#
# Shared library for dlopen absolute path test
#
if [ -f UPPERCASE.o ]; then rm UPPERCASE.o
fi
gcc -c -fpic UPPERCASE.c
if [ -f UPPERCASE.so ]; then rm UPPERCASE.so
fi
gcc -shared -lc -o UPPERCASE.so UPPERCASE.o
#
# Shared library for dlopen relative path test
#
export LD_LIBRARY_PATH=`pwd`
if [ -f lowercase.o ]; then rm lowercase.o
fi
gcc -c -fpic lowercase.c
if [ -f lowercase.so ]; then rm lowercase.so
fi
gcc -shared -lc -o lowercase.so lowercase.o
#
# Rebuild test program
#
if [ -f dlTest ]; then rm dlTest
fi
gcc -o dlTest dlTest.c -ldl
echo Current LD_LIBRARY_PATH=$LD_LIBRARY_PATH
dlTest
Summary
Creating shared objects that can be dynamically linked at runtime to an application on a Linux
system is a simple exercise. The application gains access to the shared objects by using function
calls dlopen, dlsym, and dlclose to the dynamic linking loader. Any errors are returned in strings,
by dlerror, which describe the last error encountered by a dl function. At runtime, the main
application finds the shared object libraries using either an absolute path or a path relative to
LD_LIBRARY_PATH and requests the address of the needed dll entry points. Indirect function
calls are made to the dlls as needed, and finally the handle to the shared objects is closed and the
objects are unmapped from memory and made unavailable.
The shared objects are compiled with an additional option, -fpic or -fPIC, to generate position-
independent code and placed into a shared object library with the -shared option.
Shared object libraries and the dynamic linking loader available in Linux provides additional
capabilities to applications. It reduces the size of executable files on the disk and in memory.
Optional application functions can be loaded as needed, defect can be fixed without rebuilding
the complete application, and third-party plugins can included in the application.
/*************************************************************/
/* Test Linux Dynamic Function Loading */
/* */
/* void *dlopen(const char *filename, int flag)
*/
/* Opens dynamic library and return handle */
/* */
/* const char *dlerror(void) */
/* Returns string describing the last error.
*/
/* */
/* void *dlsym(void *handle, char *symbol) */
/* Return pointer to symbol's load point.
*/
/* If symbol is undefined, NULL is returned.
*/
/* */
/* int dlclose (void *handle)
*/
/* Close the dynamic library handle.
*/
/* */
/* */
/* */
/*************************************************************/
#include<stdio.h>
#include <stdlib.h>
/* */
/* 1-dll include file and variables */
/* */
#include <dlfcn.h>
void *FunctionLib; /* Handle to shared lib file */
int (*Function)(); /* Pointer to loaded routine */
const char *dlError; /* Pointer to error string */
main( argc, argv )
{
int rc; /* return codes
*/
char HelloMessage[] = "HeLlO WoRlD\n";
/* */
/* 2-print the original message */
/* */
printf(" dlTest 2-Original message \n");
printf("%s", HelloMessage);
/* */
/* 3-Open Dynamic Loadable Libary with absolute path */
/* */
FunctionLib = dlopen("/home/dlTest/UPPERCASE.so",RTLD_LAZY);
dlError = dlerror();
printf(" dlTest 3-Open Library with absolute path return-%s- \n",
dlError);
if( dlError ) exit(1);
/* */
/* 4-Find the first loaded function */
/* */
Function = dlsym( FunctionLib, "printUPPERCASE");
dlError = dlerror();
printf(" dlTest 4-Find symbol printUPPERCASE return-%s- \n", dlError);
if( dlError ) exit(1);
/* */
/* 5-Execute the first loaded function */
/* */
rc = (*Function)( HelloMessage );
printf(" dlTest 5-printUPPERCASE return-%s- \n", dlError);
/* */
/* 6-Close the shared library handle */
/* Note: after the dlclose, "printUPPERCASE" is not loaded */
/* */
rc = dlclose(FunctionLib);
dlError = dlerror();
printf(" dlTest 6-Close handle return-%s-\n",dlError);
if( rc ) exit(1);
/* */
/* 7-Open Dynamic Loadable Libary using LD_LIBRARY path */
/* */
FunctionLib = dlopen("lowercase.so",RTLD_LAZY);
dlError = dlerror();
printf(" dlTest 7-Open Library with relative path return-%s- \n",
dlError);
if( dlError ) exit(1);
/* */
/* 8-Find the second loaded function */
/* */
Function = dlsym( FunctionLib, "printLowercase");
dlError = dlerror();
printf(" dlTest 8-Find symbol printLowercase return-%s- \n", dlError);
if( dlError ) exit(1);
/* */
/* 8-execute the second loaded function */
/* */
rc = (*Function)( HelloMessage );
printf(" dlTest 9-printLowercase return-%s- \n", dlError);
/* */
/* 10-Close the shared library handle */
/* */
rc = dlclose(FunctionLib);
dlError = dlerror();
printf(" dlTest 10-Close handle return-%s-\n",dlError);
if( rc ) exit(1);
return(0);
}
/************************************************/
/* Function to print input string as UPPER case. */
/* Returns 1.
*/
/*********************************************** */
inptr = inLine;
outptr = UPstring;
while ( *inptr != '\0' )
*outptr++ = toupper(*inptr++);
*outptr++ = '\0';
printf(UPstring);
return(1);
}
/********************************************/
/* Function to print input string as lower case. */
/* Returns 2. */
/******************************************* */
int printLowercase( inLine )
char inLine[];
{
char lowstring[256];
char *inptr, *outptr;
inptr = inLine;
outptr = lowstring;
while ( *inptr != '' )
*outptr++ = tolower(*inptr++);
*outptr++ = '';
printf(lowstring);
return(2);
}
Experiment Number: 10
OBJECTIVES:
THEORY:
Introduction:
A debugger or debugging tool is a computer program that is used to test and debug other
programs (the "target" program). The code to be examined might alternatively be running on an
instruction set simulator (ISS), a technique that allows great power in its ability to halt when
specific conditions are encountered but which will typically be somewhat slower than executing
the code directly on the appropriate (or the same) processor. Some debuggers offer two modes of
operation—full or partial simulation—to limit this impact.
Features:
Typically, debuggers also offer more sophisticated functions such as running a program
step by step (single-stepping or program animation), stopping (breaking) (pausing the program
to examine the current state) at some event or specified instruction by means of a breakpoint, and
tracking the values of variables. Some debuggers have the ability to modify program state while
it is running. It may also be possible to continue execution at a different location in the program
to bypass a crash or logical error.
The same functionality which makes a debugger useful for eliminating bugs allows it to be used
as a software cracking tool to evade copy protection, digital rights management, and other
software protection features. It often also makes it useful as a general verification tool, test
coverage and performance analyzer, especially if instruction path lengths are shown.
Most mainstream debugging engines, such as gdb and dbx, provide console-based
command line interfaces. Debugger front-ends are popular extensions to debugger engines that
provide IDE integration, program animation, and visualization features. Some early mainframe
debuggers such as Oliver and SIMON provided this same functionality for the IBM System/360
and later operating systems, as long ago as the 1970s.
Most modern microprocessors have at least one of these features in their CPU design to make
debugging easier:
List of debuggers:
Allinea DDT
GNU Debugger (GDB)
Intel Debugger (IDB)
LLDB
Microsoft Visual Studio Debugger
Valgrind
WinDbg
There are two debugging tools. The first is a trace mechanism that can be used on the
global functions in the top level loop. The second tool is a debugger that is not used in the
normal top level loop. After a first program run it is possible to go back to breakpoints,
and to inspect values or to restart certain functions with different arguments. This second
tool only runs under Unix, because it duplicates the running process via a fork .
Trace:
The trace of a function is the list of the values of its parameters together with its result
in the course of a program run.
The trace commands are directives in the toplevel loop. They allow to trace a function,
stop its trace or to stop all active traces. These three directives are shown in the table
below.
#trace name trace function name
#untrace name stop tracing function name
#untrace_all stop all traces
# let f x = x + 1;;
val f : int -> int = <fun>
# f 4;;
- : int = 5
Now we will trace this function, so that its arguments and its return value will be shown.
# #trace f;;
f is now traced.
# f 4;;
f <-- 4
f --> 5
- : int = 5
Passing of the argument 4 to f is shown, then the function f calculates the desired
value and the result is returned and also shown. The arguments of a function call are
indicated by a left arrow and the return value by an arrow to the right.
Functions of Several Arguments:
Functions of several arguments (or functions returning a closure) are also traceable.
Each argument passed is shown. To distinguish the different closures, the number of
arguments already passed to the closures is marked with a *. Let the function verif_div
take 4 numbers (a, b, q, r) corresponding to the integer division: a = bq + r.
# let verif_div a b q r =
a = b*q + r;;
val verif_div : int -> int -> int -> int -> bool = <fun>
# verif_div 11 5 2 1;;
- : bool = true
# #trace verif_div;;
verif_div is now traced.
# verif_div 11 5 2 1;;
verif_div <-- 11
verif_div --> <fun>
verif_div* <-- 5
verif_div* --> <fun>
verif_div** <-- 2
verif_div** --> <fun>
verif_div*** <-- 1
verif_div*** --> true
- : bool = true
Debug:
ocamldebug, is a debugger in the usual sense of the word. It permits step-by-step
execution, the insertion of breakpoints and the inspection and modification of values in the
environment.
Warning
Take the following example file fact.ml which calculates the factorial function:
let fact n =
let rec fact_aux p q n =
if n = 0 then p
else fact_aux (p+q) p (n-1)
in
fact_aux 1 1 n;;
The main program in the file main.ml goes off on a long recursion after the call of Fact.fact on -
1.
$ ocamldebug fact.exe
Objective Caml Debugger version 3.00
(ocd)
Execution Control
Execution control is done via program events. It is possible to go forward and backwards by n
program events, or to go forward or backwards to the next breakpoint (or the nth breakpoint). A
breakpoint can be set on a function or a program event. The choice of language element is shown
by line and column number or the number of characters. This locality may be relative to a
module.
In the example below, a breakpoint is set at the fourth line of module Main:
(ocd) step 0
Loading program... done.
Time : 0
Beginning of program.
(ocd) break @ Main 4
Breakpoint 1 at 5028 : file Main, line 4 column 3
The initialisations of the module are done before the actual program. This is the reason the
breakpoint at line 4 occurs only after 5028 instructions.
To continue our example, we go forward to the breakpoint and then execute three program
instructions:
(ocd) run
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4 <|b|>Fact.fact !x;;
(ocd) step
Time : 7 - pc : 4860 - module Fact
2 <|b|>let rec fact_aux p q n =
(ocd) step
Time : 8 - pc : 4876 - module Fact
6 <|b|>fact_aux 1 1 n;;
(ocd) step
Time : 9 - pc : 4788 - module Fact
3 <|b|>if n = 0 then p
Inspection of Values
At a breakpoint, the values of variables in the activation record can be inspected. The print and
display commands output the values associated with a variable according to the different depths.
We will print the value of n, then go back three steps to print the contents of x:
(ocd) print n
n : int = -1
(ocd) backstep 3
Time : 6 - pc : 4964 - module Main
Breakpoint : 1
4 <|b|>Fact.fact !x;;
(ocd) print x
x : int ref = {contents=-1}
Access to the fields of a record or via the index of an array is accepted by the printing
commands.
The execution stack permits a visualization of the entanglement of function invocations. The
backtrace or bt command shows the stack of calls. The up and down commands select the next or
preceding activation record. Finally, the frame command gives a description of the current
record.
FAQs.