18
18
#include "../attr.h"
19
19
#include "../string-list.h"
20
20
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
+
21
189
#define HCAST (type , handle ) ((type)(intptr_t)handle)
22
190
23
191
void open_in_gdb (void )
@@ -529,6 +697,21 @@ int mingw_unlink(const char *pathname)
529
697
*/
530
698
if (!_wrmdir (wpathname ))
531
699
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
532
715
} while (retry_ask_yes_no (& tries , "Unlink of file '%s' failed. "
533
716
"Should I try again?" , pathname ));
534
717
return -1 ;
@@ -803,6 +986,10 @@ int mingw_open (const char *filename, int oflags, ...)
803
986
if (fd >= 0 && set_hidden_flag (wfilename , 1 ))
804
987
warning ("could not mark '%s' as hidden." , filename );
805
988
}
989
+ #ifdef DEBUG_FILE_LOCKS
990
+ if (fd >= 0 )
991
+ alloc_file_lock_backtrace (fd , filename );
992
+ #endif
806
993
return fd ;
807
994
}
808
995
@@ -858,6 +1045,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
858
1045
errno = ENOENT ;
859
1046
if (file && hide && set_hidden_flag (wfilename , 1 ))
860
1047
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
861
1052
return file ;
862
1053
}
863
1054
@@ -866,6 +1057,9 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
866
1057
int hide = needs_hiding (filename );
867
1058
FILE * file ;
868
1059
wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
1060
+ #ifdef DEBUG_FILE_LOCKS
1061
+ int oldfd = fileno (stream );
1062
+ #endif
869
1063
if (filename && !strcmp (filename , "/dev/null" ))
870
1064
wcscpy (wfilename , L"nul" );
871
1065
else if (!is_valid_win32_path (filename , 1 )) {
@@ -885,9 +1079,37 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
885
1079
file = _wfreopen (wfilename , wotype , stream );
886
1080
if (file && hide && set_hidden_flag (wfilename , 1 ))
887
1081
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
888
1088
return file ;
889
1089
}
890
1090
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
+
891
1113
#undef fflush
892
1114
int mingw_fflush (FILE * stream )
893
1115
{
@@ -2698,6 +2920,26 @@ int mingw_rename(const char *pold, const char *pnew)
2698
2920
SetFileAttributesW (wpnew , attrs & ~FILE_ATTRIBUTE_READONLY ))
2699
2921
goto repeat ;
2700
2922
}
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
2701
2943
if (retry_ask_yes_no (& tries , "Rename from '%s' to '%s' failed. "
2702
2944
"Should I try again?" , pold , pnew ))
2703
2945
goto repeat ;
@@ -3954,6 +4196,10 @@ int wmain(int argc, const wchar_t **wargv)
3954
4196
3955
4197
SetConsoleCtrlHandler (handle_ctrl_c , TRUE);
3956
4198
4199
+ #ifdef DEBUG_FILE_LOCKS
4200
+ initialize_file_lock_map ();
4201
+ #endif
4202
+
3957
4203
maybe_redirect_std_handles ();
3958
4204
adjust_symlink_flags ();
3959
4205
fsync_object_files = 1 ;
0 commit comments