File handler

From Msim

Jump to: navigation, search

The page refers to a the file handler that is part of each thread that retains information related to the currently open file descriptors related to that file.

Contents

Rationale

Previously, FILE* was used to retain redirected input and redirect output (stdout only, stderr in M-sim 3.0). With one thread, this isn't a problem (as long as all file redirections were handled properly). With multiple threads, input and output can clash unless the redirections were provided by the .arg file. When running checkpoints, file descriptor values could clash when they are loaded - or completely lose their redirection associations.

Prior Structures

  • FILE* infile, outfile, errfile

These handled input and output (stdout and stderr). When making system calls that use a file, redirection to these pointers is required.

File descriptor 0 (stdin) redirects to infile
File descriptor 1 (stdout) redirects to outfile
File descriptor 2 (stderr) redirects to errfile

New Structures

Each thread now have tables with the information related to their own files. Unless a checkpoint is made, the only file descriptors that will need redirection are stdin, stdout and stderr. When loading checkpoints, file descriptors are not longer guaranteed to have the same value.
Additionally, we retain filename information that is not typically part of a file descriptor. This is for debugging purposes.
In smt.h, class context

std::map<md_gpr_t, md_gpr_t> translation_table;
std::vector<std::pair<md_gpr_t,std::string> > name_table;

translation_table maps the value from the source register (data type: md_gpr_t, this is the expected file descriptor from the program's point of view) into the correct redirected value
name_table maps to a filename instead (or "stdin", "stdout", "stderr" where appropriate).

Translation is achieved by the following member function added to smt.h, class context

bool replace_with_redirect(md_gpr_t & source);

This overwrites the register input with the correct value. Returns true if a redirection occurred, otherwise, returns false (the register input is not changed if this returns false). This is used in syscall.c to redirect file pointers as well as retain the original file descriptor value. (If we redirected, revert the register to the original value before finishing the operation).

Initialization

At context creation, translation_table and name_table are initialized as follows (smt.c, context()):

       translation_table[0] = 0;
       translation_table[1] = 1;
       translation_table[2] = 2;
       name_table.push_back(std::make_pair(0,"stdin"));
       name_table.push_back(std::make_pair(1,"stdout"));
       name_table.push_back(std::make_pair(2,"stderr"));

This indicates that there are no redirections taking place. 0 points to 0, 1 points to 1 and 2 points to 2. The names are placed into the name table.

Destruction

At context destruction, we scan through the translation_table and close any file descriptors except stdin, stdout and stderr. (smt.c ~context()):

       for(std::map<md_gpr_t, md_gpr_t>::iterator it = translation_table.begin(); it!=translation_table.end(); it++)
       {
               if(((*it).second != 0) && ((*it).second != 1) && ((*it).second != 2))
               {
                       close((*it).second);
               }
       }

Initial Redirections

Instead of assigning the redirections to infile, outfile and errfile, we can assign them to our translation table. From main.c, init_thread(...):
For stdin:

                       FILE* temp = fopen(buffer,"r");
                       contexts[curcontext].translation_table[0] = fileno(temp);
                       if(temp == NULL)
                       {
                               std::cerr << " - failed: couldn't open for reading" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << contexts[curcontext].translation_table[0] << std::endl;
                       contexts[curcontext].name_table[0].second = buffer;

This replaces the older code:

                       contexts[curcontext].infile = fopen(buffer,"r");
                       if(contexts[curcontext].infile == NULL)
                       {
                               std::cerr << " - failed: couldn't open for reading" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << fileno(contexts[curcontext].infile) << std::endl;

For stdout:

                       FILE* temp = fopen(buffer,"w");
                       contexts[curcontext].translation_table[1] = fileno(temp);
                       if(temp == NULL)
                       {
                               std::cerr << " - failed: couldn't open for writing" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << contexts[curcontext].translation_table[1] << std::endl;
                       contexts[curcontext].name_table[1].second = buffer;

This replaces the older code:

                       contexts[curcontext].outfile = fopen(buffer,"w");
                       if(contexts[curcontext].outfile == NULL)
                       {
                               std::cerr << " - failed: couldn't open for writing" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << fileno(contexts[curcontext].outfile) << std::endl;

For stderr:

                       FILE* temp = fopen(buffer,"w");
                       contexts[curcontext].translation_table[2] = fileno(temp);
                       if(temp == NULL)
                       {
                               std::cerr << " - failed: couldn't open for writing" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << contexts[curcontext].translation_table[2] << std::endl;
                       contexts[curcontext].name_table[2].second = buffer;

This replaces the older code:

                       contexts[curcontext].errfile = fopen(buffer,"w");
                       if(contexts[curcontext].errfile == NULL)
                       {
                               std::cerr << " - failed: couldn't open for writing" << std::endl;
                               exit(1);
                       }
                       std::cerr << " fd: " << fileno(contexts[curcontext].errfile) << std::endl;

Global Redirection

In main.c, global redirection of simulated stdout and simulated stderr can be done using -redir:prog and -redir:err. If these are used, instead of assigning FILE* outfile and errfile to match the redirection, we use the translation_table instead.

               if(sim_progfd)
               {
                       contexts[i].translation_table[1] = fileno(sim_progfd);
                       contexts[i].name_table[1].second = sim_progout;
               }
               if(sim_progerrfd)
               {
                       contexts[i].translation_table[2] = fileno(sim_progerrfd);
                       contexts[i].name_table[2].second = sim_progerr;
               }

This replaces the older code:

               if(sim_progfd)
               {
                       contexts[i].outfile = sim_progfd;
               }
               if(sim_progerrfd)
               {
                       contexts[i].errfile = sim_progerrfd;
               }

Handling Syscalls

The redirection check has been simplified to (the code is just as complex, if not a little more complex):

               redirected = contexts[context_id].replace_with_redirect(regs->regs_R[MD_REG_A0]);

Instead of:

               if(regs->regs_R[MD_REG_A0] == 0 && contexts[regs->context_id].infile)
               {
                       regs->regs_R[MD_REG_A0] = fileno(contexts[regs->context_id].infile);
                       redirected = true;
               }
               if((regs->regs_R[MD_REG_A0] == 1 && contexts[regs->context_id].outfile))
               {
                       regs->regs_R[MD_REG_A0] = fileno(contexts[regs->context_id].outfile);
                       redirected = true;
               }
               if((regs->regs_R[MD_REG_A0] == 2 && contexts[regs->context_id].errfile))
               {
                       regs->regs_R[MD_REG_A0] = fileno(contexts[regs->context_id].errfile);
                       redirected = true;
               }



With this implementation, we need to track the opening and closing of files.

OSF_SYS_open

If the file was opened successfully. Open file shown here:

                       //open the file
                       regs->regs_R[MD_REG_V0] = open(buf, local_flags, /*mode*/regs->regs_R[MD_REG_A2]);

Then we insert into the translation table:

                       if(regs->regs_R[MD_REG_V0] != (qword_t)-1)
                       {
                               contexts[context_id].translation_table[regs->regs_R[MD_REG_V0]] = regs->regs_R[MD_REG_V0];
                               contexts[context_id].name_table.push_back(std::make_pair(regs->regs_R[MD_REG_V0],buf));
                       }

OSF_SYS_close

If the file was closed successfully. Close file shown here:

               //close the file
               regs->regs_R[MD_REG_V0] = close(/*fd*/regs->regs_R[MD_REG_A0]);

Then we remove from the translation table:

               if(regs->regs_R[MD_REG_V0] != (qword_t)-1)
               {
                       for(std::map<md_gpr_t, md_gpr_t>::iterator it = contexts[context_id].translation_table.begin(); it!=contexts[context_id].translation_table.end();it++)
                       {
                               if((*it).second == regs->regs_R[MD_REG_A0])
                               {
                                       contexts[context_id].translation_table.erase(it);
                                       break;
                               }
                       }
                       for(unsigned int i=0;i<contexts[context_id].name_table.size();i++)
                       {
                               if(contexts[context_id].name_table[i].first == regs->regs_R[MD_REG_A0])
                               {
                                       contexts[context_id].name_table.erase(contexts[context_id].name_table.begin()+i);
                                       break;
                               }
                       }
               }

Note: This code can probably be instrumented a bit further but we haven't bothered with that at this time.

Checkpoints

Checkpoint files can be created that retain the file handler information at creation time. These allow us to restore file descriptors and their internal positions within the file. The implementation of this will be part of EIO version 4.

Personal tools