1818#include "../attr.h"
1919#include "../string-list.h"
2020
21+ #ifdef DEBUG_FILE_LOCKS
22+
23+ #include "libbacktrace/backtrace.h"
24+ #include "../hashmap.h"
25+
26+ struct file_lock_backtrace
27+ {
28+ struct hashmap_entry entry ;
29+ int fd , count ;
30+ uintptr_t * pcs ;
31+ const char * filename ;
32+ };
33+
34+ static CRITICAL_SECTION backtrace_mutex ;
35+ static struct hashmap file_lock_map ;
36+ #define FILE_LOCK_MAX_FD 256
37+ static struct file_lock_backtrace * file_lock_by_fd [FILE_LOCK_MAX_FD ];
38+
39+ static int my_backtrace_cb (void * data , uintptr_t pc , const char * filename ,
40+ int lineno , const char * function )
41+ {
42+ struct strbuf * buf = data ;
43+
44+ if (!function || !strcmp ("__tmainCRTStartup" , function ))
45+ return -1 ;
46+
47+ strbuf_addf (buf , "%s:%d:\n\t%s\n" , filename , lineno , function );
48+
49+ return 0 ;
50+ }
51+
52+ static void my_error_cb (void * data , const char * msg , int errnum )
53+ {
54+ struct strbuf * buf = data ;
55+
56+ strbuf_addf (buf , "error %s (%d)\n" , msg , errnum );
57+ }
58+
59+ static void file_lock_backtrace (struct file_lock_backtrace * data ,
60+ struct strbuf * buf )
61+ {
62+ static struct backtrace_state * state ;
63+ static int initialized ;
64+ int i ;
65+
66+ if (!initialized ) {
67+ EnterCriticalSection (& backtrace_mutex );
68+ if (!initialized ) {
69+ state = backtrace_create_state (NULL , 1 , my_error_cb ,
70+ NULL );
71+ initialized = 1 ;
72+ }
73+ LeaveCriticalSection (& backtrace_mutex );
74+ }
75+
76+ if (data -> fd >= 0 )
77+ strbuf_addf (buf , "file '%s' (fd %d) was opened here:\n" ,
78+ data -> filename , data -> fd );
79+ for (i = 0 ; i < data -> count ; i ++ )
80+ if (backtrace_pcinfo (state , data -> pcs [i ], my_backtrace_cb ,
81+ my_error_cb , buf ) < 0 )
82+ break ;
83+ }
84+
85+ static struct file_lock_backtrace * alloc_file_lock_backtrace (int fd ,
86+ const char * filename )
87+ {
88+ DECLARE_PROC_ADDR (kernel32 .dll , USHORT , NTAPI , RtlCaptureStackBackTrace ,
89+ ULONG , ULONG , PVOID * , PULONG );
90+ struct file_lock_backtrace * result ;
91+ uintptr_t pcs [62 ];
92+ int count = 0 ;
93+ size_t pcs_size = 0 , size , len = strlen (filename ) + 1 ;
94+
95+ if ((fd < 0 || fd >= FILE_LOCK_MAX_FD ) && fd != -123 )
96+ BUG ("Called with fd = %d\n" , fd );
97+
98+ if (INIT_PROC_ADDR (RtlCaptureStackBackTrace )) {
99+ count = RtlCaptureStackBackTrace (1 , ARRAY_SIZE (pcs ),
100+ (void * * )pcs , NULL );
101+ pcs_size = sizeof (uintptr_t ) * count ;
102+ }
103+ size = sizeof (* result ) + pcs_size + len + 1 ;
104+
105+ result = xmalloc (size );
106+ result -> fd = fd ;
107+ result -> count = count ;
108+ if (!count )
109+ result -> pcs = NULL ;
110+ else {
111+ result -> pcs = (uintptr_t * )((char * )result + sizeof (* result ));
112+ memcpy (result -> pcs , pcs , pcs_size );
113+ }
114+
115+ result -> filename = ((char * )result + sizeof (* result ) + pcs_size );
116+ strlcpy ((char * )result -> filename , filename , len );
117+
118+ if (fd < 0 )
119+ return result ;
120+
121+ EnterCriticalSection (& backtrace_mutex );
122+ if (file_lock_by_fd [fd ]) {
123+ struct strbuf buf = STRBUF_INIT ;
124+ strbuf_addf (& buf , "Bogus file_lock (%d). First trace:\n" , fd );
125+ file_lock_backtrace (file_lock_by_fd [fd ], & buf );
126+ strbuf_addf (& buf , "\nSecond trace:\n" );
127+ file_lock_backtrace (result , & buf );
128+ BUG ("%s" , buf .buf );
129+ }
130+ file_lock_by_fd [fd ] = result ;
131+ hashmap_entry_init (& result -> entry , strihash (filename ));
132+ hashmap_add (& file_lock_map , & result -> entry );
133+ LeaveCriticalSection (& backtrace_mutex );
134+
135+ return result ;
136+ }
137+
138+ static void current_backtrace (struct strbuf * buf )
139+ {
140+ struct file_lock_backtrace * p = alloc_file_lock_backtrace (-123 , "" );
141+ file_lock_backtrace (p , buf );
142+ free (p );
143+ }
144+
145+ static void remove_file_lock_backtrace (int fd )
146+ {
147+ if (fd < 0 || fd >= FILE_LOCK_MAX_FD )
148+ BUG ("Called with fd = %d\n" , fd );
149+
150+ EnterCriticalSection (& backtrace_mutex );
151+ if (!file_lock_by_fd [fd ])
152+ BUG ("trying to release non-existing lock for fd %d" , fd );
153+
154+ hashmap_remove (& file_lock_map , & file_lock_by_fd [fd ]-> entry , NULL );
155+ free (file_lock_by_fd [fd ]);
156+ file_lock_by_fd [fd ] = NULL ;
157+ LeaveCriticalSection (& backtrace_mutex );
158+ }
159+
160+ static int file_lock_backtrace_cmp (const void * dummy ,
161+ const struct file_lock_backtrace * a ,
162+ const struct file_lock_backtrace * b ,
163+ const void * keydata )
164+ {
165+ return strcasecmp (a -> filename ,
166+ keydata ? (const char * )keydata : b -> filename );
167+ }
168+
169+ static struct file_lock_backtrace * file_lock_lookup (const char * filename )
170+ {
171+ struct file_lock_backtrace entry , * result ;
172+
173+ hashmap_entry_init (& entry .entry , strihash (filename ));
174+ EnterCriticalSection (& backtrace_mutex );
175+ result = hashmap_get_entry (& file_lock_map , & entry , entry , filename );
176+ LeaveCriticalSection (& backtrace_mutex );
177+
178+ return result ;
179+ }
180+
181+ static void initialize_file_lock_map (void )
182+ {
183+ InitializeCriticalSection (& backtrace_mutex );
184+ hashmap_init (& file_lock_map , (hashmap_cmp_fn )file_lock_backtrace_cmp ,
185+ NULL , 0 );
186+ }
187+ #endif
188+
21189#define HCAST (type , handle ) ((type)(intptr_t)handle)
22190
23191void open_in_gdb (void )
@@ -529,6 +697,21 @@ int mingw_unlink(const char *pathname)
529697 */
530698 if (!_wrmdir (wpathname ))
531699 return 0 ;
700+ #ifdef DEBUG_FILE_LOCKS
701+ {
702+ struct file_lock_backtrace * p =
703+ file_lock_lookup (pathname );
704+ if (p ) {
705+ struct strbuf buf = STRBUF_INIT ;
706+ strbuf_addf (& buf , "the file '%s' wants "
707+ "to be deleted here:\n" , pathname );
708+ current_backtrace (& buf );
709+ strbuf_addf (& buf , "\nBut it is still open:\n" );
710+ file_lock_backtrace (p , & buf );
711+ die ("%s\n" , buf .buf );
712+ }
713+ }
714+ #endif
532715 } while (retry_ask_yes_no (& tries , "Unlink of file '%s' failed. "
533716 "Should I try again?" , pathname ));
534717 return -1 ;
@@ -803,6 +986,10 @@ int mingw_open (const char *filename, int oflags, ...)
803986 if (fd >= 0 && set_hidden_flag (wfilename , 1 ))
804987 warning ("could not mark '%s' as hidden." , filename );
805988 }
989+ #ifdef DEBUG_FILE_LOCKS
990+ if (fd >= 0 )
991+ alloc_file_lock_backtrace (fd , filename );
992+ #endif
806993 return fd ;
807994}
808995
@@ -858,6 +1045,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
8581045 errno = ENOENT ;
8591046 if (file && hide && set_hidden_flag (wfilename , 1 ))
8601047 warning ("could not mark '%s' as hidden." , filename );
1048+ #ifdef DEBUG_FILE_LOCKS
1049+ if (file )
1050+ alloc_file_lock_backtrace (fileno (file ), filename );
1051+ #endif
8611052 return file ;
8621053}
8631054
@@ -866,6 +1057,9 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
8661057 int hide = needs_hiding (filename );
8671058 FILE * file ;
8681059 wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
1060+ #ifdef DEBUG_FILE_LOCKS
1061+ int oldfd = fileno (stream );
1062+ #endif
8691063 if (filename && !strcmp (filename , "/dev/null" ))
8701064 wcscpy (wfilename , L"nul" );
8711065 else if (!is_valid_win32_path (filename , 1 )) {
@@ -885,9 +1079,37 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
8851079 file = _wfreopen (wfilename , wotype , stream );
8861080 if (file && hide && set_hidden_flag (wfilename , 1 ))
8871081 warning ("could not mark '%s' as hidden." , filename );
1082+ #ifdef DEBUG_FILE_LOCKS
1083+ if (file ) {
1084+ remove_file_lock_backtrace (oldfd );
1085+ alloc_file_lock_backtrace (fileno (file ), filename );
1086+ }
1087+ #endif
8881088 return file ;
8891089}
8901090
1091+ #ifdef DEBUG_FILE_LOCKS
1092+ #undef close
1093+ int mingw_close (int fd )
1094+ {
1095+ int ret = close (fd );
1096+ if (!ret )
1097+ remove_file_lock_backtrace (fd );
1098+ return ret ;
1099+ }
1100+ #define close mingw_close
1101+
1102+ #undef fclose
1103+ int mingw_fclose (FILE * stream )
1104+ {
1105+ int fd = fileno (stream ), ret = fclose (stream );
1106+ if (!ret )
1107+ remove_file_lock_backtrace (fd );
1108+ return ret ;
1109+ }
1110+ #define fclose mingw_fclose
1111+ #endif
1112+
8911113#undef fflush
8921114int mingw_fflush (FILE * stream )
8931115{
@@ -2698,6 +2920,26 @@ int mingw_rename(const char *pold, const char *pnew)
26982920 SetFileAttributesW (wpnew , attrs & ~FILE_ATTRIBUTE_READONLY ))
26992921 goto repeat ;
27002922 }
2923+ #ifdef DEBUG_FILE_LOCKS
2924+ {
2925+ struct file_lock_backtrace * p = file_lock_lookup (pnew );
2926+ const char * which = "target" ;
2927+ if (!p ) {
2928+ p = file_lock_lookup (pold );
2929+ which = "source" ;
2930+ }
2931+ if (p ) {
2932+ struct strbuf buf = STRBUF_INIT ;
2933+ strbuf_addf (& buf , "the file '%s' wants to be "
2934+ "renamed to '%s' here:\n" , pold , pnew );
2935+ current_backtrace (& buf );
2936+ strbuf_addf (& buf , "\nBut the %s is still open:\n" ,
2937+ which );
2938+ file_lock_backtrace (p , & buf );
2939+ die ("%s\n" , buf .buf );
2940+ }
2941+ }
2942+ #endif
27012943 if (retry_ask_yes_no (& tries , "Rename from '%s' to '%s' failed. "
27022944 "Should I try again?" , pold , pnew ))
27032945 goto repeat ;
@@ -3954,6 +4196,10 @@ int wmain(int argc, const wchar_t **wargv)
39544196
39554197 SetConsoleCtrlHandler (handle_ctrl_c , TRUE);
39564198
4199+ #ifdef DEBUG_FILE_LOCKS
4200+ initialize_file_lock_map ();
4201+ #endif
4202+
39574203 maybe_redirect_std_handles ();
39584204 adjust_symlink_flags ();
39594205 fsync_object_files = 1 ;
0 commit comments