@@ -55,6 +55,8 @@ DARSHAN_FORWARD_DECL(creat64, int, (const char* path, mode_t mode));
5555DARSHAN_FORWARD_DECL (dup , int , (int oldfd ));
5656DARSHAN_FORWARD_DECL (dup2 , int , (int oldfd , int newfd ));
5757DARSHAN_FORWARD_DECL (dup3 , int , (int oldfd , int newfd , int flags ));
58+ DARSHAN_FORWARD_DECL (fcntl , int , (int , int , ...));
59+ DARSHAN_FORWARD_DECL (fcntl64 , int , (int , int , ...));
5860DARSHAN_FORWARD_DECL (fileno , int , (FILE * stream ));
5961DARSHAN_FORWARD_DECL (mkstemp , int , (char * template ));
6062DARSHAN_FORWARD_DECL (mkostemp , int , (char * template , int flags ));
@@ -787,6 +789,85 @@ int DARSHAN_DECL(dup3)(int oldfd, int newfd, int flags)
787789 return (ret );
788790}
789791
792+ /* wrapping fcntl is a little strange for two related reasons:
793+ * - the code can do a lot of different things based on 'cmd'
794+ * - some of those 'cmd' (not all) take a third argument
795+ *
796+ * There are some worrying notes in the documentation about calling va_args
797+ * when there's no third (variable) argument, but we observed glibc doing that
798+ * and our testing seems to indicate it's ok to do so.
799+ *
800+ * So we'll always grab the variable argument, even if it's not there, and
801+ * always pass whatever we get to the real fcntl. Then we'll figure out if the
802+ * command was something we should log or not, and update our stats accordingly
803+ */
804+
805+ int DARSHAN_DECL (fcntl )(int fd , int cmd , ...)
806+ {
807+ int ret ;
808+ double tm1 , tm2 ;
809+ va_list arg ;
810+ void * next ;
811+
812+ MAP_OR_FAIL (fcntl );
813+
814+ va_start (arg , cmd );
815+ next = va_arg (arg , void * );
816+ va_end (arg );
817+
818+ tm1 = POSIX_WTIME ();
819+ ret = __real_fcntl (fd , cmd , next );
820+ tm2 = POSIX_WTIME ();
821+
822+ struct posix_file_record_ref * rec_ref ;
823+
824+ /* some code (e.g. python) prefers (portabilty? functionality?) to
825+ * duplicate the file descriptor via fcntl instead of dup/dup2/dup3 */
826+ if (ret >= 0 && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC )) {
827+ POSIX_PRE_RECORD ();
828+ rec_ref = darshan_lookup_record_ref (posix_runtime -> fd_hash ,
829+ & fd , sizeof (fd ));
830+
831+ POSIX_RECORD_REFOPEN (ret , rec_ref , tm1 , tm2 , POSIX_DUPS );
832+ POSIX_POST_RECORD ();
833+ }
834+
835+ return ret ;
836+ }
837+
838+ int DARSHAN_DECL (fcntl64 )(int fd , int cmd , ...)
839+ {
840+ int ret ;
841+ double tm1 , tm2 ;
842+ va_list arg ;
843+ void * next ;
844+
845+ MAP_OR_FAIL (fcntl64 );
846+
847+ va_start (arg , cmd );
848+ next = va_arg (arg , void * );
849+ va_end (arg );
850+
851+ tm1 = POSIX_WTIME ();
852+ ret = __real_fcntl64 (fd , cmd , next );
853+ tm2 = POSIX_WTIME ();
854+
855+ struct posix_file_record_ref * rec_ref ;
856+
857+ /* this is a duplicate of code in fcntl, but when I try to break this out
858+ * into a separate 'fcntl_common' routine, POSIX_PRE_RECORD() macro cannot
859+ * find the __darshan_disabled variable, and furthermore I get weird
860+ * "failed to seek" errors. Am I going to have to make this a macro? */
861+ if (ret >= 0 && (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC )) {
862+ POSIX_PRE_RECORD ();
863+ rec_ref = darshan_lookup_record_ref (posix_runtime -> fd_hash ,
864+ & fd , sizeof (fd ));
865+ POSIX_RECORD_REFOPEN (ret , rec_ref , tm1 , tm2 , POSIX_DUPS );
866+ POSIX_POST_RECORD ();
867+ }
868+
869+ return ret ;
870+ }
790871int DARSHAN_DECL (fileno )(FILE * stream )
791872{
792873 int ret ;
0 commit comments