DDD Student Documentation

What is DDD

DDD stands for Data Display Debugger. You cannot edit your source code in DDD. DDD is just a graphical front-end for GDB and other command-line debuggers (called inferior debuggers, because it lies at the layer beneath DDD). The purpose of a debugger such as DDD is to allow you to see what is going on "inside" a program while it executes. You can set breakpoints, print values of variables, etc.

Installing and Running DDD

Edit your .cshrc file in your home directory and add following line at the end of your file

setenv PATH /auto/usc/ddd/3.2.1/bin:$PATH

Type
source   .cshrc

to make that change effective for your current login session. From this point on, you can just type 'ddd' from any directory in your account and it will execute.

Using DDD to debug Nachos

The main thing you have to do is to follow the simple test case provided to you. It shows how the threads are executed without synchronization. After you are done with installing and compiling the nachos and ddd, the main thing you have to do is to follow the instructions for running the simple default test case provided to you.

  1. Go to the directory nachos-csci402/code/threads.
  2. From this directory type the following command to start ddd:
    ddd nachos&

    Before going further let me explain a few things in general and for main.cc in particular.

    Global Variables: All global kernel variables are to be defined in the threads directory files system.h and system.cc. All the predefined Nachos global kernel variables are defined here. Any new ones that you need for any of the projects are to also be defined here. Follow the pattern that already exists: In system.h, you have an extern statement, the actual declaration goes in system.cc. If you declar a pointer variable, you need a 'new' statement in the Initialize function in system.cc.

    These are:

    Thread *currentThread; // the thread we are running now
    Thread *threadToBeDestroyed;  // the thread that just finished
    Scheduler *scheduler; // the ready list
    Interrupt *interrupt; // interrupt status
    Statistics *stats; // performance metrics
    Timer *timer; // the hardware timer device
    

    The Initialize function:The Initialize function is also implemented in system.cc. What it does is take the argument variables from the command prompt and invokes particular functions and
    initializes Nacos kernel variables. Take a look inside this function to see which variables are initialized. Notice that it is all the global kernel variables which are defined as pointers.

    Macro Guards: You will encounter various macro guards #ifdef --- #endif at various places in Nachos. These are used for defining the particular functions and variables associated with THREADS (project1) for threads, USER_PROGRAM (project2) for user programs, USE_TLB and USE_VM (project3) use of TLB for virtual memory, NETWORK (project4) for Nachos networking, and others.

    Extern functions and variables:C/C++, be default, only allows a variable to have scope in the file in which it is declared. Nachos has lots of files with course code and all the global kernel variables need to be available to every Nachos source code file. The way this is achieved is to use the extern statement. For example, the statement:

    extern void threadtest();
    
    is implemented in threadtest.cc in threads directory, but it is called in main.cc.

  3. Now, set the breakpoint at the threadtest() function. You can do it by pressing left click on the line where threadtest(); is written and then press Breakat from the bottom menu of the ddd screen. A stop sign will appear on that line.
  4. In the program menu at the top of ddd screen press the 'run' menu option. A small window will appear. Make sure your cursor is within the text field and enter the following:
    -P -d a
    

    -P will run the default Nachos test case
    -d will display the debug messages as to what's happening.
  5. Press the okay button. The cursor (-> sign)will stop at the place where you put the breakpoint.

    From the separate small ddd menu , you can press "step" to go inside the threadtest function. Stepping through will take you to the each detail of the function being called during the program execution.

    The "Next" command on that menu will only execute the next line but will not take you inside a called function. It will execute the entire function.

  6. Now, in the threadtest() function in threadtest.cc You will see the following:
    1. It prints a debug message
    2. It initializes a new thread pointer by giving it a name.In our case it is "forked thread".Checks the thread constructor in thread.h/thread.cc
    3. Calls the Fork member function of the Thread class. The Fork function takes the pointer of a function as its first argumenet and an integer value to be passed to that funtion. So, it takes "SimpleThread" as its first argument and "0" as the second argument which is actually the value of the only argument in the SimpleThread function. This causes a new execution stream to be created and placed on the Ready Queue.
    4. Then currentThread->Yield() is called . What this does is put the thread that is currently running in the CPU, which is "main", to theback of the Ready Queue. It then takes the thread which is at the front of the Ready Queue and gives it the CPU.

      Guess what we have at the front of the Ready Queue?. It is the new thread that we created which is going to run the code in the SimpleThread function with an argumentvalue of 1. This function is being associated with the thread named "forked thread".
    5. When the new thread gets into the CPU, it will execute the code to print the message 'thread 1 looped 0 times.'
    6. Again currenthread->Yield() will be called, so, main thread will again come to play and message printed will be "thread 0 looped 1 times"
    7. Eventually, thread 0 is looped five times and thread 1 is looped five times.
    8. In the end
      currentthread->Finish()
      is called. This method terminates threads when they have executed all their code.

What is a Segmentation fault?

An error in which a running program attempts to access memory not allocated to it (or tries to use memory that is allocated to it in an incorrect way) and core dumps with a segmentation violation error. This is often caused by improper usage of pointers in the source code, dereferencing a null pointer, or (in C) inadvertently using a non-pointer variable as a pointer.