Debugging ifstream

I am debugging a function and as usual I always dump the memory to check if the address of a variable that I used actually contains the correct data.

Anyway, I do the same thing when I assign a file to a file pointer variable. in my function I opened the file mFile1.txt. and in the "watch1" window it show
-fileNames[0]	0x004e4b02 "mFile1.txt"	char * const

So I can say that fileNames[0] and "mFiles1.txt" are one and the same.

Then I went to the "command window" and display the first 32 bytes pointed by the address "0x4e4b02" using the command "db /count:32 0x4e4b02". This 32 bytes represents the first 32 bytes contents of the file.
and this is what I got. what it constains is something I am not interested, I only want to be sure there is something in it to read and display.

>db /count:32 0x004e4b02
0x004E4B02  6d 46 69 6c 65 31 2e 74 78 74 00 6d 46 69 6c 65  
0x004E4B12  32 2e 74 78 74 00 6d 46 69 6c 65 33 2e 74 78 74 


Then a declare a variable "ret" of type (ifstream which has an address of "0x004e4b7c" in the "watch1 window". if you notice there is a prefix "+" on variable "ret" indication that I could expand it and see the entire class.
+ret	0x004e4b7c {_Filebuffer={...} }	std::basic_ifstream<char,std::char_traits<char> > *


then on the command window I dump the first 32bytes starting from that address.
>db /count:32 0x004e4b7c
0x004E4B7C  9c 09 f1 00 cd cd cd cd 00 00 00 00 00 00 00 00  
0x004E4B8C  ac 09 f1 00 68 4f 4e 00 00 00 00 00 00 00 00 00 


the first four bytes in the dumps memory of address 0x004e4b7c is an address
0x00f1099c so I also look on that memory location.

db /count:32 0x00f1099c
0x00F1099C  00 00 00 00 68 00 00 00 00 00 00 00 64 1e f1 00  
0x00F109AC  92 14 f0 00 fa 15 f0 00 37 15 f0 00 67 12 f0 00 


I have ran this program and it is working correctly so "ret" must be pointing to the correct file.

Since "ret" is related to "fileNames[0] somewhere in the dumped memory of ret, I should find the address of fileNames[0]. This is a concern to me because I do not have anything that I could tie "ret" and the file name "mFile1.txt". Making the program run correctly is OK to me but I want fool proof that will tell me that the two variables are related.

My question is in the watch1 window where would I find the address of mFile1.txt under the name "ret"? If I expand "ret" where would I go? I am attaching the main program and the functions for your perusal.please note that the main requires 5 arguments which are file names, those files should be existing if you want to run the program. If not the program will terminate gracefully without executing the main code.

MAIN PROGRAM:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <cstdlib>
#include <iostream>
#include <fstream>
using namespace std;

ifstream *OpenFiles(char * const fileNames[], size_t count);
void MergeAndDisplay(ifstream files[], size_t count);

int main(int argc, char *argv[])
{
   const int FILE_NAME1_ARG_NO = 1;    // command line arg: first file name

   ifstream *files = OpenFiles(&argv[FILE_NAME1_ARG_NO], size_t(argc - 1));
   MergeAndDisplay(files, size_t(argc - 1));
   delete[] files;
   cin.get();
   return EXIT_SUCCESS;
}
#endif 


FUNCTIONS CALLED BY THE MAIN.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <fstream>
#include <iostream>
using namespace std;

//Constant Declaration global to this file
const int SIZE_OF_IFSTREAM = 5;
const int SIZE_OF_CHAR_STORAGE = 16;
const char EMPTY[] = "";

// Open files as per specification as describe on the title above
ifstream *OpenFiles(char * const fileNames[], size_t count)
{ 
    //Variable declaration and initialization of ifstream pointer and 
    //dynamically allocate memory
    ifstream *ret = new ifstream[count]; 
   
    // If no command line arguments, error and exit we compared the count to 1 instead of 0
    // because the executable is argument #1 which should always be there.
    if (count <= 1) 
    {
        cerr << "There is no arguments.\n";
        //display message and wait for response before exiting
        cin.get();
        exit(EXIT_FAILURE);
    }

    //opens the files in the main program arguments 
    int loopCount;
    int filecntr = (int)count;
    while (--filecntr >= 0)
    { 
        //open the file in a read optional second argument
        ret[filecntr].open(fileNames[filecntr]);
        //if any of the files fails to open close all the files and exit
        if (!ret[filecntr].is_open()) 
        { 
            cerr << "Failed to open " << fileNames[count] << "\n";
            for (loopCount = --filecntr; loopCount >= 0; loopCount--) 
            {
                ret[loopCount].close(); 
                cout << "Closed " << fileNames[loopCount] << "\n";
                count = 0;
            }
        } 
    }
    return ret;
}

// Obtain string in one line of each file and display those 1 line per file
void MergeAndDisplay(ifstream files[], size_t count)
{
    // function variable declaration
    char fileOneLine[SIZE_OF_CHAR_STORAGE];
    bool fileisEmpty[SIZE_OF_IFSTREAM], allFilesClosed = false;
    unsigned int boolCntr;

    //Initialize the file "empty flag"
    for (boolCntr = 0; boolCntr < count; boolCntr++) 
        fileisEmpty[boolCntr] = false;
    
    //Enter the title of the table
    cout << "The Display Would Be\n\n";

    //get the data from each file and display it.
    //for (unsigned int lineCntr = 1; !allFilesClosed; ++lineCntr)
    while (!allFilesClosed)
    {
        //processing only one line of each file
        for (unsigned int fileCntr = 0; fileCntr < count; ++fileCntr)
        {   
            //when the file has been closed it is no longer used and only the remaining files are process
            if (fileisEmpty[fileCntr])
                continue;
            //obtain the line of each file and store it in a character storage to be process.
            files[fileCntr].getline(fileOneLine, SIZE_OF_CHAR_STORAGE, '\n'); 

            //process this when the line read in the file is empty
            if (strcmp(fileOneLine, EMPTY)) 
                cout << fileOneLine << '\n'; 
            else
            //when the line is empty, it is assumed to have reached the EOF and this section is executed
            //only once for the same file
            if (!fileisEmpty[fileCntr])
            {
                files[fileCntr].close();
                fileisEmpty[fileCntr] = true;
            } 
        } 
        //exit the process when all files are closed
        allFilesClosed = true;
        //check if all the files are closed, if it is exit the program
        for (boolCntr = 0; boolCntr < count; boolCntr++) 
            if (!fileisEmpty[boolCntr])
                allFilesClosed = false;
    }
    return;
}

 
You cannot tie an std::ifstream object with the name of the file that was opened by just examining your program data, in the usual cases.

An ifstream object contains a filebuf object, which contains a FILE*, which points at a structure that usually contains an open file descriptor. The mapping between open file descriptors and the file names is maintained by the OS.

On Linux, for example, I would do the following:
(gdb) print ret->_M_filebuf._M_file._M_cfile->_fileno
$10 = 7
(gdb) info program
        Using the running image of child process 40922.
(gdb) shell ls -l /proc/40922/fd/7
lr-x------ 1 szubkov general 64 Nov 12 16:12 /proc/40922/fd/7 -> /home11/szubkov/test/test.txt

this tells me that "ret" is pointing to test/test.txt in my home directory (which was the file I opened for this test)

Naturally, the way to get to the file descriptor in the debugger is highly implementation-dependent. For example, IBM, it is ret->_Fb._File->_file, on Sun ret->__fb.__file or ret->_M_buf._M_base._M_file_id depending on the stdlib chosen.
Last edited on
Ok in Linux you are able to show that "ret" is pointing to test/test.txt in your home directory (which was the file you opened for this test).

You just prove to me that you could tie the two together. This removes any doubt, about the relation between ret and test.txt.

So when you say print->ret it meant print what is the content of test.txt.

Is this something I could do in C++. Without saying I put a blind trust on the operating system? Can I say ret is relatest to test.txt because I could see the address of test.txt being pointed by a pointer somewhere in ret?
You just prove to me that you could tie the two together.

Yes, by asking the OS.

Can I say ret is relatest to test.txt because I could see the address of test.txt being pointed by a pointer somewhere in ret

There isn't anything like that in fstream objects on the implementations I know.
Last edited on
Topic archived. No new replies allowed.