I'm using a library in my C++ application and trying to capture all the output in a file. I tried to redirect the stderr to stdout and then stdout to a file like so:
./a.out 2>&1 > out.txt
This captures pretty much everything in my application but there are still some output on the console related to the library I'm using. My question is:
Are there anything besides stdout/stderr? (other than stdin)
If there are, how can I identify these in my case?
And then how can I redirect these to the same file?
Note: In case someone is familiar, library is called SystemC (which is an event driven simulation library/language on top of C++ for mainly system/hardware design).
You must set output file before any stream-to-stream redirection, else bash can't detect file name to output. In your case you can see stderr output.
See bash redirections reference manual.
Solution:
./a.out >out.txt 2>&1
Or just:
./a.out &>out.txt
Hmm, well I think what might be happening is that your program is printing to the controlling terminal. One possibility could be to have your program run as a daemon with no controlling terminal. I have a C function that I call to turn my code into a daemon, I got this from a book called The Linux Programming Interface which I highly recommend.
#define BD_NO_CHDIR 01 /* Don't chdir("/") */
#define BD_NO_CLOSE_FILES 02 /* Don't close all open files */
#define BD_NO_REOPEN_STD_FDS 04 /* Don't reopen stdin, stdout, and
stderr to /dev/null */
#define BD_NO_UMASK0 010 /* Don't do a umask(0) */
#define BD_MAX_CLOSE 8192 /* Maximum file descriptors to close if
sysconf(_SC_OPEN_MAX) is indeterminate */
int becomeDaemon(int flags){
int maxfd, fd, new_stdout;
switch (fork()) { /* Become background process */
case -1: return -1;
case 0: break; /* Child falls through... */
default: _exit(EXIT_SUCCESS); /* while parent terminates */
}
if (setsid() == -1) /* Become leader of new session */
return -1;
switch (fork()) { /* Ensure we are not session leader */
case -1: return -1;
case 0: break;
default: _exit(EXIT_SUCCESS);
}
if (!(flags & BD_NO_UMASK0))
umask(0); /* Clear file mode creation mask */
if (!(flags & BD_NO_CHDIR))
chdir("/"); /* Change to root directory */
if (!(flags & BD_NO_CLOSE_FILES)) { /* Close all open files */
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd == -1) /* Limit is indeterminate... */
maxfd = BD_MAX_CLOSE; /* so take a guess */
for (fd = 0; fd < maxfd; fd++)
close(fd);
}
if (!(flags & BD_NO_REOPEN_STD_FDS)) {
/*
STDIN = 0
STDOUT = 1
STDERR = 2
*/
close(0); /* Reopen standard fd's to /dev/null */
fd = open("/dev/null", O_RDWR);
if (fd != 0) /* 'fd' should be 0 */
return -1;
if (dup2(0, 1) != 1)
return -1;
if (dup2(0, 2) != 2)
return -1;
}
return 0;
}
Now I suppose that you can change the line open("/dev/null", O_RDWR) to open("/home/you/output.txt", O_RDWR) and redirect the output there. Ofcourse then you wouldn't be able to directly input from the terminal to your program, but from the sounds of the error message you're getting I think you're using a socket anyway so could possible write a client to do that for you if it was necessary.
Hope that helps.
Related
In the following program I print to the console using two different functions
#include <windows.h>
int main() {
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD byteswritten;
WriteConsole(h, "WriteConsole", 12, &byteswritten, NULL);
WriteFile(h, "WriteFile", 9, &byteswritten, NULL);
}
If when I execute this program and redirect it's output using a > out.txt or a 1> out.txt nothing gets printed to the console (as expected) but the contents of out.txt are only
WriteFile
What is different between the two that allows calls to WriteFile to be redirected to the file and calls to WriteConsole to go to ... nowhere
Tested with gcc and msvc on windows 10
WriteConsole only works with console screen handles, not files nor pipes.
If you are only writing ASCII content you can use WriteFile for everything.
If you need to write Unicode characters you can use GetConsoleMode to detect the handle type, it fails for everything that is not a console handle.
When doing raw output like this you also have to deal with the BOM if the handle is redirected to a file.
This blog post is a good starting point for dealing with Unicode in the Windows console...
Edit 2021:
Windows 10 now has the ConPTY API (aka pseudo-console), which basically allows any program to act like the console for another program, thus enables capturing output that is directly written to the console.
This renders my original answer obsolete for Windows versions that support ConPTY.
Original answer:
From the reference:
WriteConsole fails if it is used with a standard handle that is
redirected to a file. If an application processes multilingual output
that can be redirected, determine whether the output handle is a
console handle (one method is to call the GetConsoleMode function and
check whether it succeeds). If the handle is a console handle, call
WriteConsole. If the handle is not a console handle, the output is
redirected and you should call WriteFile to perform the I/O.
This is only applicable if you control the source code of the application that you want to redirect. I recently had to redirect output from a closed-source application that unconditionally called WriteConsole() so it could not be redirected normally.
Reading the console screen buffer (as suggested by this answer) prooved to be unreliable, so I used Microsoft Detours library to hook the WriteConsole() API in the target process and call WriteFile() if necessary. Otherwise call the original WriteConsole() function.
I created a hook DLL based on the example of Using Detours:
#include <windows.h>
#include <detours.h>
// Target pointer for the uninstrumented WriteConsoleW API.
//
auto WriteConsoleW_orig = &WriteConsoleW;
// Detour function that replaces the WriteConsoleW API.
//
BOOL WINAPI WriteConsoleW_hooked(
_In_ HANDLE hConsoleOutput,
_In_ const VOID *lpBuffer,
_In_ DWORD nNumberOfCharsToWrite,
_Out_ LPDWORD lpNumberOfCharsWritten,
_Reserved_ LPVOID lpReserved
)
{
// Check if this actually is a console screen buffer handle.
DWORD mode;
if( GetConsoleMode( hConsoleOutput, &mode ) )
{
// Forward to the original WriteConsoleW() function.
return WriteConsoleW_orig( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, lpReserved );
}
else
{
// This is a redirected handle (e. g. a file or a pipe). We multiply with sizeof(WCHAR), because WriteFile()
// expects the number of bytes, but WriteConsoleW() gets passed the number of characters.
BOOL result = WriteFile( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite * sizeof(WCHAR), lpNumberOfCharsWritten, nullptr );
// WriteFile() returns number of bytes written, but WriteConsoleW() has to return the number of characters written.
if( lpNumberOfCharsWritten )
*lpNumberOfCharsWritten /= sizeof(WCHAR);
return result;
}
}
// DllMain function attaches and detaches the WriteConsoleW_hooked detour to the
// WriteConsoleW target function. The WriteConsoleW target function is referred to
// through the WriteConsoleW_orig target pointer.
//
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
if (DetourIsHelperProcess()) {
return TRUE;
}
if (dwReason == DLL_PROCESS_ATTACH) {
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)WriteConsoleW_orig, WriteConsoleW_hooked);
DetourTransactionCommit();
}
else if (dwReason == DLL_PROCESS_DETACH) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)WriteConsoleW_orig, WriteConsoleW_hooked);
DetourTransactionCommit();
}
return TRUE;
}
Note: In the WriteFile() branch I don't write a BOM (byte order mark), because it is not always wanted (e. g. when redirecting to a pipe instead of a file or when appending to an existing file). An application that is using the DLL to redirect process output to a file can simply write the UTF-16 LE BOM on its own before launching the redirected process.
The target process is created using DetourCreateProcessWithDllExW(), specifying the name of our hook DLL as argument for the lpDllName parameter. The other arguments are identical to how you create a redirected process via the CreateProcessW() API. I won't go into detail, because these are all well documented.
The code below can be used to redirect console output if the other party uses WriteConsole. The code reads the output via a hidden console screen buffer. I've written this code to intercept debug output some directshow drivers write to the console. Directshow drivers have the habit of doing things drivers should not do, like writing unwanted logfiles, writing to console and crashing.
// info to redirected console output
typedef struct tagRedConInfo
{
// hidden console
HANDLE hCon;
// old console handles
HANDLE hOldConOut;
HANDLE hOldConErr;
// buffer to read screen content
CHAR_INFO *BufData;
INT BufSize;
//
} TRedConInfo;
//------------------------------------------------------------------------------
// GLOBALS
//------------------------------------------------------------------------------
// initial handles
HANDLE gv_hOldConOut;
HANDLE gv_hOldConErr;
//------------------------------------------------------------------------------
// PROTOTYPES
//------------------------------------------------------------------------------
/* init redirecting the console output */
BOOL Shell_InitRedirectConsole(BOOL,TRedConInfo*);
/* done redirecting the console output */
BOOL Shell_DoneRedirectConsole(TRedConInfo*);
/* read string from hidden console, then clear */
BOOL Shell_ReadRedirectConsole(TRedConInfo*,TCHAR*,INT);
/* clear buffer of hidden console */
BOOL Shell_ClearRedirectConsole(TRedConInfo*);
//------------------------------------------------------------------------------
// IMPLEMENTATIONS
//------------------------------------------------------------------------------
/***************************************/
/* init redirecting the console output */
/***************************************/
BOOL Shell_InitRedirectConsole(BOOL in_SetStdHandles, TRedConInfo *out_RcInfo)
{
/* locals */
HANDLE lv_hCon;
SECURITY_ATTRIBUTES lv_SecAttr;
// preclear structure
memset(out_RcInfo, 0, sizeof(TRedConInfo));
// prepare inheritable handle just in case an api spans an external process
memset(&lv_SecAttr, 0, sizeof(SECURITY_ATTRIBUTES));
lv_SecAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
lv_SecAttr.bInheritHandle = TRUE;
// create hidden console buffer
lv_hCon = CreateConsoleScreenBuffer(
GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
&lv_SecAttr, CONSOLE_TEXTMODE_BUFFER, 0);
// failed to create console buffer?
if (lv_hCon == INVALID_HANDLE_VALUE)
return FALSE;
// store
out_RcInfo->hCon = lv_hCon;
// set as standard handles for own process?
if (in_SetStdHandles)
{
// mutex the globals
WaitForGlobalVarMutex();
// remember the old handles
out_RcInfo->hOldConOut = GetStdHandle(STD_OUTPUT_HANDLE);
out_RcInfo->hOldConErr = GetStdHandle(STD_ERROR_HANDLE);
// set hidden console as std output
SetStdHandle(STD_OUTPUT_HANDLE, lv_hCon);
SetStdHandle(STD_ERROR_HANDLE, lv_hCon);
// is this the first instance?
if (!gv_hOldConOut)
{
// inform our own console output code about the old handles so our own
// console will be writing to the real console, only console output from
// other parties will write to the hidden console
gv_hOldConOut = out_RcInfo->hOldConOut;
gv_hOldConErr = out_RcInfo->hOldConErr;
}
// release mutex
ReleaseGlobalVarMutex();
}
// done
return TRUE;
}
/***************************************/
/* done redirecting the console output */
/***************************************/
BOOL Shell_DoneRedirectConsole(TRedConInfo *in_RcInfo)
{
// validate
if (!in_RcInfo->hCon)
return FALSE;
// restore original handles?
if (in_RcInfo->hOldConOut)
{
// mutex the globals
WaitForGlobalVarMutex();
// restore original handles
SetStdHandle(STD_OUTPUT_HANDLE, in_RcInfo->hOldConOut);
SetStdHandle(STD_ERROR_HANDLE, in_RcInfo->hOldConErr);
// was this the first instance?
if (in_RcInfo->hOldConOut == gv_hOldConOut)
{
// clear
gv_hOldConOut = NULL;
gv_hOldConErr = NULL;
}
// release mutex
ReleaseGlobalVarMutex();
}
// close the console handle
CloseHandle(in_RcInfo->hCon);
// free read buffer
if (in_RcInfo->BufData)
MemFree(in_RcInfo->BufData);
// clear structure
memset(in_RcInfo, 0, sizeof(TRedConInfo));
// done
return TRUE;
}
/***********************************************/
/* read string from hidden console, then clear */
/***********************************************/
BOOL Shell_ReadRedirectConsole(TRedConInfo *in_RcInfo, TCHAR *out_Str, INT in_MaxLen)
{
/* locals */
TCHAR lv_C;
INT lv_X;
INT lv_Y;
INT lv_W;
INT lv_H;
INT lv_N;
INT lv_Len;
INT lv_Size;
INT lv_PrvLen;
COORD lv_DstSze;
COORD lv_DstOfs;
DWORD lv_Written;
SMALL_RECT lv_SrcRect;
CHAR_INFO *lv_BufData;
CONSOLE_SCREEN_BUFFER_INFO lv_Info;
// preclear output
out_Str[0] = 0;
// validate
if (!in_RcInfo->hCon)
return FALSE;
// reserve character for eos
--in_MaxLen;
// get current buffer info
if (!GetConsoleScreenBufferInfo(in_RcInfo->hCon, &lv_Info))
return FALSE;
// check whether there is something at all
if (!lv_Info.dwSize.X || !lv_Info.dwSize.Y)
return FALSE;
// limit the buffer passed onto read call otherwise it
// will fail with out-of-resources error
lv_DstSze.X = (INT16)(lv_Info.dwSize.X);
lv_DstSze.Y = (INT16)(lv_Info.dwSize.Y < 8 ? lv_Info.dwSize.Y : 8);
// size of buffer needed
lv_Size = lv_DstSze.X * lv_DstSze.Y * sizeof(CHAR_INFO);
// is previous buffer too small?
if (!in_RcInfo->BufData || in_RcInfo->BufSize < lv_Size)
{
// free old buffer
if (in_RcInfo->BufData)
MemFree(in_RcInfo->BufData);
// allocate read buffer
if ((in_RcInfo->BufData = (CHAR_INFO*)MemAlloc(lv_Size)) == NULL)
return FALSE;
// store new size
in_RcInfo->BufSize = lv_Size;
}
// always write to (0,0) in buffer
lv_DstOfs.X = 0;
lv_DstOfs.Y = 0;
// init src rectangle
lv_SrcRect.Left = 0;
lv_SrcRect.Top = 0;
lv_SrcRect.Right = lv_DstSze.X;
lv_SrcRect.Bottom = lv_DstSze.Y;
// buffer to local
lv_BufData = in_RcInfo->BufData;
// start at first string position in output
lv_Len = 0;
// loop until no more rows to read
do
{
// read buffer load
if (!ReadConsoleOutput(in_RcInfo->hCon, lv_BufData, lv_DstSze, lv_DstOfs, &lv_SrcRect))
return FALSE;
// w/h of actually read content
lv_W = lv_SrcRect.Right - lv_SrcRect.Left + 1;
lv_H = lv_SrcRect.Bottom - lv_SrcRect.Top + 1;
// remember previous position
lv_PrvLen = lv_Len;
// loop through rows of buffer
for (lv_Y = 0; lv_Y < lv_H; ++lv_Y)
{
// reset output position of current row
lv_N = 0;
// loop through columns
for (lv_X = 0; lv_X < lv_W; ++lv_X)
{
// is output full?
if (lv_Len + lv_N > in_MaxLen)
break;
// get character from screen buffer, ignore attributes
lv_C = lv_BufData[lv_Y * lv_DstSze.X + lv_X].Char.UnicodeChar;
// append character
out_Str[lv_Len + lv_N++] = lv_C;
}
// remove spaces at the end of the line
while (lv_N > 0 && out_Str[lv_Len+lv_N-1] == ' ')
--lv_N;
// if row was not blank
if (lv_N > 0)
{
// update output position
lv_Len += lv_N;
// is output not full?
if (lv_Len + 2 < in_MaxLen)
{
// append cr/lf
out_Str[lv_Len++] = '\r';
out_Str[lv_Len++] = '\n';
}
}
}
// update screen position
lv_SrcRect.Top = (INT16)(lv_SrcRect.Top + lv_H);
lv_SrcRect.Bottom = (INT16)(lv_SrcRect.Bottom + lv_H);
// until nothing is added or no more screen rows
} while (lv_PrvLen != lv_Len && lv_SrcRect.Bottom < lv_Info.dwSize.Y);
// remove last cr/lf
if (lv_Len > 2)
lv_Len -= 2;
// append eos
out_Str[lv_Len] = 0;
// total screen buffer size in characters
lv_Size = lv_Info.dwSize.X * lv_Info.dwSize.Y;
// clear the buffer with spaces
FillConsoleOutputCharacter(in_RcInfo->hCon, ' ', lv_Size, lv_DstOfs, &lv_Written);
// reset cursor position to (0,0)
SetConsoleCursorPosition(in_RcInfo->hCon, lv_DstOfs);
// done
return TRUE;
}
/**********************************/
/* clear buffer of hidden console */
/**********************************/
BOOL Shell_ClearRedirectConsole(TRedConInfo *in_RcInfo)
{
/* locals */
INT lv_Size;
COORD lv_ClrOfs;
DWORD lv_Written;
CONSOLE_SCREEN_BUFFER_INFO lv_Info;
// validate
if (!in_RcInfo->hCon)
return FALSE;
// get current buffer info
if (!GetConsoleScreenBufferInfo(in_RcInfo->hCon, &lv_Info))
return FALSE;
// clear from (0,0) onward
lv_ClrOfs.X = 0;
lv_ClrOfs.Y = 0;
// total screen buffer size in characters
lv_Size = lv_Info.dwSize.X * lv_Info.dwSize.Y;
// clear the buffer with spaces
FillConsoleOutputCharacter(in_RcInfo->hCon, ' ', lv_Size, lv_ClrOfs, &lv_Written);
// reset cursor position to (0,0)
SetConsoleCursorPosition(in_RcInfo->hCon, lv_ClrOfs);
// done
return TRUE;
}
Background
I have some very complex application. It is composition of couple libraries.
Now QA team found the some problem (something reports an error).
Fromm logs I can see that application is leaking a file descriptors (+1000 after 7 hours of automated tests).
QA team has delivered rapport "opened files and ports" from "Activity monitor" and I know exactly to which server connection is not closed.
From full application logs I can see that leak is quite systematic (there is no sudden burst), but I was unable to reproduce issue to see even a small leak of file descriptors.
Problem
Even thou I'm sure for which server connection is never closed, I'm unable to find code responsible.
I'm unable reproduce issue.
In logs I can see that all resources my library maintains are properly freed, still server address suggest this is my responsibility or NSURLSession (which is invalidated).
Since there are other libraries and application code it self there is small chance that leak is caused by third party code.
Question
How to locate code responsible for leaking file descriptor?
Best candidate is use dtruss which looks very promising.
From documentation I can see it can print stack backtraces -s when system API is used.
Problem is that I do not know how to use this in such way that I will not get flooded with information.
I need only information who created opened file descriptor and if it was closed destroyed.
Since I can't reproduce issue I need a script which could be run by QA team so the could deliver me an output.
If there are other ways to find the source of file descriptor leak please let me know.
There is bunch of predefined scripts which are using dtruss, but I don't see anything what is matching my needs.
Final notes
What is strange the only code I'm aware is using problematic connection, do not use file descriptors directly, but uses custom NSURLSession (configured as: one connection per host, minimum TLS 1.0, disable cookies, custom certificate validation). From logs I can see NSURLSession is invalidated properly. I doubt NSURLSession is source of leak, but currently this is the only candidate.
OK, I found out how to do it - on Solaris 11, anyway. I get this output (and yes, I needed root on Solaris 11):
bash-4.1# dtrace -s fdleaks.d -c ./fdLeaker
open( './fdLeaker' ) returned 3
open( './fdLeaker' ) returned 4
open( './fdLeaker' ) returned 5
falloc fp: ffffa1003ae56590, fd: 3, saved fd: 3
falloc fp: ffffa10139d28f58, fd: 4, saved fd: 4
falloc fp: ffffa10030a86df0, fd: 5, saved fd: 5
opened file: ./fdLeaker
leaked fd: 3
libc.so.1`__systemcall+0x6
libc.so.1`__open+0x29
libc.so.1`open+0x84
fdLeaker`main+0x2b
fdLeaker`_start+0x72
opened file: ./fdLeaker
leaked fd: 4
libc.so.1`__systemcall+0x6
libc.so.1`__open+0x29
libc.so.1`open+0x84
fdLeaker`main+0x64
fdLeaker`_start+0x72
The fdleaks.d dTrace script that finds leaked file descriptors:
#!/usr/sbin/dtrace
/* this will probably need tuning
note there can be significant performance
impacts if you make these large */
#pragma D option nspec=4
#pragma D option specsize=128k
#pragma D option quiet
syscall::open*:entry
/ pid == $target /
{
/* arg1 might not have a physical mapping yet so
we can't call copyinstr() until open() returns
and we don't have a file descriptor yet -
we won't get that until open() returns anyway */
self->path = arg1;
}
/* arg0 is the file descriptor being returned */
syscall::open*:return
/ pid == $target && arg0 >= 0 && self->path /
{
/* get a speculation ID tied to this
file descriptor and start speculative
tracing */
openspec[ arg0 ] = speculation();
speculate( openspec[ arg0 ] );
/* this output won't appear unless the associated
speculation id is commited */
printf( "\nopened file: %s\n", copyinstr( self->path ) );
printf( "leaked fd: %d\n\n", arg0 );
ustack();
/* free the saved path */
self->path = 0;
}
syscall::close:entry
/ pid == $target && arg0 >= 0 /
{
/* closing the fd, so discard the speculation
and free the id by setting it to zero */
discard( openspec[ arg0 ] );
openspec[ arg0 ] = 0;
}
/* Solaris uses falloc() to open a file and associate
the fd with an internal file_t structure
When the kernel closes file descriptors that the
process left open, it uses the closeall() function
which walks the internal structures then calls
closef() using the file_t *, so there's no way
to get the original process file descritor in
closeall() or closef() dTrace probes.
falloc() is called on open() to associate the
file_t * with a file descriptor, so this
saves the pointers passed to falloc()
that are used to return the file_t * and
file descriptor once they're filled in
when falloc() returns */
fbt::falloc:entry
/ pid == $target /
{
self->fpp = args[ 2 ];
self->fdp = args[ 3 ];
}
/* Clause-local variables to make casting clearer */
this int fd;
this uint64_t fp;
/* array to associate a file descriptor with its file_t *
structure in the kernel */
int fdArray[ uint64_t fp ];
fbt::falloc:return
/ pid == $target && self->fpp && self->fdp /
{
/* get the fd and file_t * values being
returned to the caller */
this->fd = ( * ( int * ) self->fdp );
this->fp = ( * ( uint64_t * ) self->fpp );
/* associate the fd with its file_t * */
fdArray[ this->fp ] = ( int ) this->fd;
/* verification output */
printf( "falloc fp: %x, fd: %d, saved fd: %d\n", this->fp, this->fd, fdArray[ this->fp ] );
}
/* if this gets called and the dereferenced
openspec array element is a still-valid
speculation id, the fd associated with
the file_t * passed to closef() was never
closed by the process itself */
fbt::closef:entry
/ pid == $target /
{
/* commit the speculative tracing since
this file descriptor was leaked */
commit( openspec[ fdArray[ arg0 ] ] );
}
First, I wrote this little C program to leak fds:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main( int argc, char **argv )
{
int ii;
for ( ii = 0; ii < argc; ii++ )
{
int fd = open( argv[ ii ], O_RDONLY );
fprintf( stderr, "open( '%s' ) returned %d\n", argv[ ii ], fd );
fd = open( argv[ ii ], O_RDONLY );
fprintf( stderr, "open( '%s' ) returned %d\n", argv[ ii ], fd );
fd = open( argv[ ii ], O_RDONLY );
fprintf( stderr, "open( '%s' ) returned %d\n", argv[ ii ], fd );
close( fd );
}
return( 0 );
}
Then I ran it under this dTrace script to figure out what the kernel does to close orphaned file descriptors, dtrace -s exit.d -c ./fdLeaker:
#!/usr/sbin/dtrace -s
#pragma D option quiet
syscall::rexit:entry
{
self->exit = 1;
}
syscall::rexit:return
/ self->exit /
{
self->exit = 0;
}
fbt:::entry
/ self->exit /
{
printf( "---> %s\n", probefunc );
}
fbt:::return
/ self->exit /
{
printf( "<--- %s\n", probefunc );
}
That produced a lot of output, and I noticed closeall() and closef() functions, examined the source code, and wrote the dTrace script.
Note also that the process exit dTrace probe on Solaris 11 is the rexit one - that probably changes on OSX.
The biggest problem on Solaris is getting the file descriptor for the file in the kernel code that closes orphaned file descriptors. Solaris doesn't close by file descriptor, it closes by struct file_t pointers in the kernel open files structures for the process. So I had to examine the Solaris source to figure out where the fd is associated with the file_t * - and that's in the falloc() function. The dTrace script associates a file_t * with its fd in an associative array.
None of that is likely to work on OSX.
If you're lucky, the OSX kernel will close orphaned file descriptors by the file descriptor itself, or at least provide something that tells you the fd is being closed, perhaps an auditing function.
I'm building a device driver of sorts that consumes data from a keyboard emulating device.
The device is a card swipe, so its behavior is as follows:
User walks up, swipes card
I get a string of characters (key codes, really, including modifier keys for capital letters)
I don't know how many characters I'm going to get
I don't know when I'm getting something
Since I don't know how many characters I'm going to get, blocking reads on the keyboard tty aren't useful - I'd end up blocking after the last character. What I'm doing is, in Ruby, using the IO module to perform async reads against the keyboard device, and using a timeout to determine that the end of data was reached. This works fine logically (even a user swiping his or her card fast will do so slower than the send rate between characters).
The issue is that sometimes, I lose data from the middle of the string. My hunch is that there's some sort of buffer overflow happening because I'm reading the data too slowly. Trying to confirm this, I inserted small waits in between each key process. Longer waits (20ms+) do exacerbate the problem. However, a wait of around 5ms actually makes it go away? The only explanation I can come up with is that the async read itself is expensive (because Ruby), and doing them without a rate limit is actually slower than doing them with a 5ms delay.
Does this sound rational? Are there other ideas on what this could be?
The ruby is actually JRuby 9000. The machine is Ubuntu LTS 16.
Edit: here's a snippet of the relevant code
private def read_swipe(buffer_size, card_reader_input, pause_between_reads, seconds_to_complete)
limit = Time.now + seconds_to_complete.seconds
swipe_data = ''
begin
start_time = Time.now
sleep pause_between_reads
batch = card_reader_input.read_nonblock(buffer_size)
swipe_data << batch
rescue IO::WaitReadable
IO.select([card_reader_input], nil, nil, 0.5)
retry unless limit < start_time
end while start_time < limit
swipe_data
end
where card_reader_input = File.new(event_handle, 'rb')
I am not sure about Ruby code but you can use linux sysfs to access the characters coming out of keyboard 'like' device, and if feasible you can call C code from ruby application. I had done this for barcode reader and following is the code:
static int init_barcode_com(char* bcr_portname)
{
int fd;
/* Open the file descriptor in non-blocking mode */
fd = open(bcr_portname, O_RDONLY | O_NOCTTY | O_NDELAY);
cout << "Barcode Reader FD: " << fd <<endl;
if (fd == -1)
{
cerr << "ERROR: Cannot open fd for barcode communication with error " << fd <<endl;
}
fcntl(fd, F_SETFL, 0);
/* Set up the control structure */
struct termios toptions;
/* Get currently set options for the tty */
tcgetattr(fd, &toptions);
/* Set custom options */
/* 9600 baud */
cfsetispeed(&toptions, B9600);
cfsetospeed(&toptions, B9600);
/* 8 bits, no parity, no stop bits */
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
/* no hardware flow control */
toptions.c_cflag &= ~CRTSCTS;
/* enable receiver, ignore status lines */
toptions.c_cflag |= CREAD | CLOCAL;
/* disable input/output flow control, disable restart chars */
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
/* disable canonical input, disable echo,
* disable visually erase chars,
* disable terminal-generated signals */
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
/* disable output processing */
toptions.c_oflag &= ~OPOST;
/* wait for n (in our case its 1) characters to come in before read returns */
/* WARNING! THIS CAUSES THE read() TO BLOCK UNTIL ALL */
/* CHARACTERS HAVE COME IN! */
toptions.c_cc[VMIN] = 0;
/* no minimum time to wait before read returns */
toptions.c_cc[VTIME] = 100;
/* commit the options */
tcsetattr(fd, TCSANOW, &toptions);
/* Wait for the Barcode to reset */
usleep(10*1000);
return fd;
}
static int read_from_barcode_reader(int fd, char* bcr_buf)
{
int i = 0, nbytes = 0;
char buf[1];
/* Flush anything already in the serial buffer */
tcflush(fd, TCIFLUSH);
while (1) {
nbytes = read(fd, buf, 1); // read a char at a time
if (nbytes == -1) {
return -1; // Couldn't read
}
if (nbytes == 0) {
return 0;
}
if (buf[0] == '\n' || buf[0] == '\r') {
return 0;
}
bcr_buf[i] = buf[0];
i++;
}
return 0;
}
Now that you do not know how many characters your going to get you can use VMIN and VTIME combination to address your concern. This document details various possibilities with VMIN and VTIME.
I wrote a function to watch a file (given an fd) growing to a certain size including a timeout. I'm using kqueue()/kevent() to wait for the file to be "extended" but after I get the notification that the file grew I have to check the file size (and compare it against the desired size). That seems to be easy but I cannot figure out a way to do that reliably in POSIX.
NB: The timeout will hit if the file doesn't grow at all for the time specified. So, this is not an absolute timeout, just a timeout that some growing happens to the file. I'm on OS X but this question is meant for "every POSIX that has kevent()/kqueue()", that should be OS X and the BSDs I think.
Here's my current version of my function:
/**
* Blocks until `fd` reaches `size`. Times out if `fd` isn't extended for `timeout`
* amount of time. Returns `-1` and sets `errno` to `EFBIG` should the file be bigger
* than wanted.
*/
int fwait_file_size(int fd,
off_t size,
const struct timespec *restrict timeout)
{
int ret = -1;
int kq = kqueue();
struct kevent changelist[1];
if (kq < 0) {
/* errno set by kqueue */
ret = -1;
goto out;
}
memset(changelist, 0, sizeof(changelist));
EV_SET(&changelist[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_EXTEND, 0, 0);
if (kevent(kq, changelist, 1, NULL, 0, NULL) < 0) {
/* errno set by kevent */
ret = -1;
goto out;
}
while (true) {
{
/* Step 1: Check the size */
int suc_sz = evaluate_fd_size(fd, size); /* IMPLEMENTATION OF THIS IS THE QUESTION */
if (suc_sz > 0) {
/* wanted size */
ret = 0;
goto out;
} else if (suc_sz < 0) {
/* errno and return code already set */
ret = -1;
goto out;
}
}
{
/* Step 2: Wait for growth */
int suc_kev = kevent(kq, NULL, 0, changelist, 1, timeout);
if (0 == suc_kev) {
/* That's a timeout */
errno = ETIMEDOUT;
ret = -1;
goto out;
} else if (suc_kev > 0) {
if (changelist[0].filter == EVFILT_VNODE) {
if (changelist[0].fflags & NOTE_RENAME || changelist[0].fflags & NOTE_DELETE) {
/* file was deleted, renamed, ... */
errno = ENOENT;
ret = -1;
goto out;
}
}
} else {
/* errno set by kevent */
ret = -1;
goto out;
}
}
}
out: {
int errno_save = errno;
if (kq >= 0) {
close(kq);
}
errno = errno_save;
return ret;
}
}
So the basic algorithm works the following way:
Set up the kevent
Check size
Wait for file growth
Steps 2 and 3 are repeated until the file reached the wanted size.
The code uses a function int evaluate_fd_size(int fd, off_t wanted_size) which will return < 0 for "some error happened or file larger than wanted", == 0 for "file not big enough yet", or > 0 for file has reached the wanted size.
Obviously this only works if evaluate_fd_size is reliable in determining file size. My first go was to implement it with off_t eof_pos = lseek(fd, 0, SEEK_END) and compare eof_pos against wanted_size. Unfortunately, lseek seems to cache the results. So even when kevent returned with NOTE_EXTEND, so the file grew, the result may be the same! Then I thought to switch to fstat but found articles that fstat caches as well.
The last thing I tried was using fsync(fd); before off_t eof_pos = lseek(fd, 0, SEEK_END); and suddenly things started working. But:
Nothing states that fsync() really solves my problem
I don't want to fsync() because of performance
EDIT: It's really hard to reproduce but I saw one case in which fsync() didn't help. It seems to take (very little) time until the file size is larger after a NOTE_EXTEND event hit user space. fsync() probably just works as a good enough sleep() and therefore it works most of the time :-.
So, in other words: How to reliably check file size in POSIX without opening/closing the file which I cannot do because I don't know the file name. Additionally, I can't find a guarantee that this would help
By the way: int new_fd = dup(fd); off_t eof_pos = lseek(new_fd, 0, SEEK_END); close(new_fd); did not overcome the caching issue.
EDIT 2: I also created an all in one demo program. If it prints Ok, success before exiting, everything went fine. But usually it prints Timeout (10000000) which manifests the race condition: The file size check for the last kevent triggered is smaller than the actual file size at this very moment. Weirdly when using ftruncate() to grow the file instead of write() it seems to work (you can compile the test program with -DUSE_FTRUNCATE to test that).
Nothing states that fsync() really solves my problem
I don't want to fsync() because of performance
Your problem isn't "fstat caching results", it's the I/O system buffering writes. Fstat doesn't get updated until the kernel flushes the I/O buffers to the underlying file system.
This is why fsync fixes your problem and any solution to your problem more or less has to do the equivalent of fsync. ( This is what the open/close solution does as a side effect. )
Can't help you with 2 because I don't see any way to avoid doing fsync.
so, I have an assignment for my Operating Systems class wherein i am to create a ring of processes connected with pipes in order to pass messages between them. i found some example code which i was looking to adapt (or at least understand) for my needs. the example code (slightly modified) is:
/* Program 4.1 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
/* Sample C program for generating a unidirectional ring of processes.Invoke this program
with a command-line arg ument indicating the number of processes on the ring. Communication
is done via pipes that connect the standard output of a process to the standard input of
its successor on the ring. After the ring is created, each process identifies itself with
its process ID and the process ID of its parent. Each process then exits. */
void main(int argc, char *argv[ ])
{
int master_pid = getpid();
printf("master pid: %i\n", master_pid);
int i; /* number of this process (starting with 1) */
int childpid; /* indicates process should spawn another */
int nprocs; /* total number of processes in ring */
int fd[2]; /* file descriptors returned by pipe */
int error; /* return value from dup2 call */
/* check command line for a valid number of processes to generate */
if ( (argc != 2) || ((nprocs = atoi (argv[1])) <= 0) ) {
fprintf (stderr, "Usage: %s nprocs\n", argv[0]);
exit(1);
}
/* connect std input to std output via a pipe */
if (pipe (fd) == -1) {
perror("Could not create pipe");
exit(1);
}
printf("%s\n", "test");
//this section is blocking printf()?
if ((dup2(fd[0], STDIN_FILENO) == -1) ||
(dup2(fd[1], STDOUT_FILENO) == -1)) {
perror("Could not dup pipes");
exit(1);
}
printf("%s\n", "test");
if ((close(fd[0]) == -1) || (close(fd[1]) == -1)) {
perror("Could not close extra descriptors");
exit(1);
}
/* create the remaining processes with their connecting pipes */
for (i = 1; i < nprocs; i++) {
if (pipe (fd) == -1) {
fprintf(stderr,"Could not create pipe %d: %s\n",
i, strerror(errno));
exit(1);
}
if ((childpid = fork()) == -1) {
fprintf(stderr, "Could not create child %d: %s\n",
i, strerror(errno));
exit(1);
}
if (childpid > 0) /* for parent process, reassign stdout */
error = dup2(fd[1], STDOUT_FILENO);
else
error = dup2(fd[0], STDIN_FILENO);
if (error == -1) {
fprintf(stderr, "Could not dup pipes for iteration %d: %s\n",
i, strerror(errno));
exit(1);
}
if ((close(fd[0]) == -1) || (close(fd[1]) == -1)) {
fprintf(stderr, "Could not close extra descriptors %d: %s\n",
i, strerror(errno));
exit(1);
}
if (childpid)
break;
}
/* say hello to the world */
fprintf(stderr,"This is process %d with ID %d and parent id %d\n",
i, (int)getpid(), (int)getppid());
wait(1);
exit (0);
} /* end of main program here */
which outputs:
master pid: 30593
test
This is process 1 with ID 30593 and parent id 30286
This is process 2 with ID 30594 and parent id 30593
when i give is 2 as argv[1]
so, I'm wondering, why would the dup2 section prevent the printf() from executing? if i cant even print something, i'm not sure if i could even pass the message correctly. also, why would the fprintf() already there work, but not one that i would put there?
edit: i would take this to my professor/TA, but theyre both out of town and will be unreachable between now and the deadline...
printf prints to stdout, which is file descriptor 1 (or equivalently STDOUT_FILENO). dup2(3) is duplicating the pipe's file descriptor on top of the current stdout, which has the side effect of closing the current stdout. So, when you try to printf after calling that particular dup2, you're really printing the data into the pipe you just created, which doesn't go to your terminal output.
fprintf(stderr, ...) still works because that prints to stderr, not stdout, and the stderr file descriptor (2, or equivalently STDERR_FILENO) does not change during the program, so it continues to print out to the terminal.
printf() does not send data to path 0, it sends buffered data using stdout. It would seem that when you disrupt path 0 by dup2'ing something to it, you're disrupting stdout in the process.
From the man page on dup2: dup2() makes newfd be the copy of oldfd, closing newfd first if necessary. Thus when you call dup2(fd[0], STDIN_FILENO) you are breaking stdout.
You state that fprintf() is working but printf() is not... what path are you using for fprintf()? If you're using stderr then it makes perfect sense that it would continue to work, since you haven't done anything with that path.