11
11
#include "../config.h"
12
12
#include "../string-list.h"
13
13
14
+ #ifdef DEBUG_FILE_LOCKS
15
+
16
+ #include "libbacktrace/backtrace.h"
17
+ #include "../hashmap.h"
18
+
19
+ struct file_lock_backtrace
20
+ {
21
+ struct hashmap_entry entry ;
22
+ int fd , count ;
23
+ uintptr_t * pcs ;
24
+ const char * filename ;
25
+ };
26
+
27
+ static CRITICAL_SECTION backtrace_mutex ;
28
+ static struct hashmap file_lock_map ;
29
+ #define FILE_LOCK_MAX_FD 256
30
+ static struct file_lock_backtrace * file_lock_by_fd [FILE_LOCK_MAX_FD ];
31
+
32
+ static int my_backtrace_cb (void * data , uintptr_t pc , const char * filename ,
33
+ int lineno , const char * function )
34
+ {
35
+ struct strbuf * buf = data ;
36
+
37
+ if (!function || !strcmp ("__tmainCRTStartup" , function ))
38
+ return -1 ;
39
+
40
+ strbuf_addf (buf , "%s:%d:\n\t%s\n" , filename , lineno , function );
41
+
42
+ return 0 ;
43
+ }
44
+
45
+ static void my_error_cb (void * data , const char * msg , int errnum )
46
+ {
47
+ struct strbuf * buf = data ;
48
+
49
+ strbuf_addf (buf , "error %s (%d)\n" , msg , errnum );
50
+ }
51
+
52
+ static void file_lock_backtrace (struct file_lock_backtrace * data ,
53
+ struct strbuf * buf )
54
+ {
55
+ static struct backtrace_state * state ;
56
+ static int initialized ;
57
+ int i ;
58
+
59
+ if (!initialized ) {
60
+ EnterCriticalSection (& backtrace_mutex );
61
+ if (!initialized ) {
62
+ state = backtrace_create_state (NULL , 1 , my_error_cb ,
63
+ NULL );
64
+ initialized = 1 ;
65
+ }
66
+ LeaveCriticalSection (& backtrace_mutex );
67
+ }
68
+
69
+ if (data -> fd >= 0 )
70
+ strbuf_addf (buf , "file '%s' (fd %d) was opened here:\n" ,
71
+ data -> filename , data -> fd );
72
+ for (i = 0 ; i < data -> count ; i ++ )
73
+ if (backtrace_pcinfo (state , data -> pcs [i ], my_backtrace_cb ,
74
+ my_error_cb , buf ) < 0 )
75
+ break ;
76
+ }
77
+
78
+ static struct file_lock_backtrace * alloc_file_lock_backtrace (int fd ,
79
+ const char * filename )
80
+ {
81
+ DECLARE_PROC_ADDR (kernel32 .dll , USHORT , RtlCaptureStackBackTrace ,
82
+ ULONG , ULONG , PVOID * , PULONG );
83
+ struct file_lock_backtrace * result ;
84
+ uintptr_t pcs [62 ];
85
+ int count = 0 ;
86
+ size_t pcs_size = 0 , size ;
87
+
88
+ if ((fd < 0 || fd >= FILE_LOCK_MAX_FD ) && fd != -123 )
89
+ BUG ("Called with fd = %d\n" , fd );
90
+
91
+ if (INIT_PROC_ADDR (RtlCaptureStackBackTrace )) {
92
+ count = RtlCaptureStackBackTrace (1 , ARRAY_SIZE (pcs ),
93
+ (void * * )pcs , NULL );
94
+ pcs_size = sizeof (uintptr_t ) * count ;
95
+ }
96
+ size = sizeof (* result ) + pcs_size + strlen (filename ) + 1 ;
97
+
98
+ result = xmalloc (size );
99
+ result -> fd = fd ;
100
+ result -> count = count ;
101
+ if (!count )
102
+ result -> pcs = NULL ;
103
+ else {
104
+ result -> pcs = (uintptr_t * )((char * )result + sizeof (* result ));
105
+ memcpy (result -> pcs , pcs , pcs_size );
106
+ }
107
+
108
+ result -> filename = ((char * )result + sizeof (* result ) + pcs_size );
109
+ strcpy ((char * )result -> filename , filename );
110
+
111
+ if (fd < 0 )
112
+ return result ;
113
+
114
+ EnterCriticalSection (& backtrace_mutex );
115
+ if (file_lock_by_fd [fd ]) {
116
+ struct strbuf buf = STRBUF_INIT ;
117
+ strbuf_addf (& buf , "Bogus file_lock (%d). First trace:\n" , fd );
118
+ file_lock_backtrace (file_lock_by_fd [fd ], & buf );
119
+ strbuf_addf (& buf , "\nSecond trace:\n" );
120
+ file_lock_backtrace (result , & buf );
121
+ BUG (buf .buf );
122
+ }
123
+ file_lock_by_fd [fd ] = result ;
124
+ hashmap_entry_init (& result -> entry , strihash (filename ));
125
+ hashmap_add (& file_lock_map , result );
126
+ LeaveCriticalSection (& backtrace_mutex );
127
+
128
+ return result ;
129
+ }
130
+
131
+ static void current_backtrace (struct strbuf * buf )
132
+ {
133
+ struct file_lock_backtrace * p = alloc_file_lock_backtrace (-123 , "" );
134
+ file_lock_backtrace (p , buf );
135
+ free (p );
136
+ }
137
+
138
+ static void remove_file_lock_backtrace (int fd )
139
+ {
140
+ if (fd < 0 || fd >= FILE_LOCK_MAX_FD )
141
+ BUG ("Called with fd = %d\n" , fd );
142
+
143
+ EnterCriticalSection (& backtrace_mutex );
144
+ if (!file_lock_by_fd [fd ])
145
+ BUG ("trying to release non-existing lock for fd %d" , fd );
146
+
147
+ hashmap_remove (& file_lock_map , file_lock_by_fd [fd ], NULL );
148
+ free (file_lock_by_fd [fd ]);
149
+ file_lock_by_fd [fd ] = NULL ;
150
+ LeaveCriticalSection (& backtrace_mutex );
151
+ }
152
+
153
+ static int file_lock_backtrace_cmp (const void * dummy ,
154
+ const struct file_lock_backtrace * a ,
155
+ const struct file_lock_backtrace * b ,
156
+ const void * keydata )
157
+ {
158
+ return strcasecmp (a -> filename ,
159
+ keydata ? (const char * )keydata : b -> filename );
160
+ }
161
+
162
+ static struct file_lock_backtrace * file_lock_lookup (const char * filename )
163
+ {
164
+ struct hashmap_entry entry ;
165
+ struct file_lock_backtrace * result ;
166
+
167
+ hashmap_entry_init (& entry , strihash (filename ));
168
+ EnterCriticalSection (& backtrace_mutex );
169
+ result = hashmap_get (& file_lock_map , & entry , filename );
170
+ LeaveCriticalSection (& backtrace_mutex );
171
+
172
+ return result ;
173
+ }
174
+
175
+ static void initialize_file_lock_map (void )
176
+ {
177
+ InitializeCriticalSection (& backtrace_mutex );
178
+ hashmap_init (& file_lock_map , (hashmap_cmp_fn )file_lock_backtrace_cmp ,
179
+ NULL , 0 );
180
+ }
181
+ #endif
182
+
14
183
#define HCAST (type , handle ) ((type)(intptr_t)handle)
15
184
16
185
int err_win_to_posix (DWORD winerr )
@@ -405,6 +574,21 @@ int mingw_unlink(const char *pathname)
405
574
*/
406
575
if (!_wrmdir (wpathname ))
407
576
return 0 ;
577
+ #ifdef DEBUG_FILE_LOCKS
578
+ {
579
+ struct file_lock_backtrace * p =
580
+ file_lock_lookup (pathname );
581
+ if (p ) {
582
+ struct strbuf buf = STRBUF_INIT ;
583
+ strbuf_addf (& buf , "the file '%s' wants "
584
+ "to be deleted here:\n" , pathname );
585
+ current_backtrace (& buf );
586
+ strbuf_addf (& buf , "\nBut it is still open:\n" );
587
+ file_lock_backtrace (p , & buf );
588
+ die ("%s\n" , buf .buf );
589
+ }
590
+ }
591
+ #endif
408
592
} while (retry_ask_yes_no (& tries , "Unlink of file '%s' failed. "
409
593
"Should I try again?" , pathname ));
410
594
return -1 ;
@@ -553,6 +737,10 @@ int mingw_open (const char *filename, int oflags, ...)
553
737
if (fd >= 0 && set_hidden_flag (wfilename , 1 ))
554
738
warning ("could not mark '%s' as hidden." , filename );
555
739
}
740
+ #ifdef DEBUG_FILE_LOCKS
741
+ if (fd >= 0 )
742
+ alloc_file_lock_backtrace (fd , filename );
743
+ #endif
556
744
return fd ;
557
745
}
558
746
@@ -601,6 +789,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
601
789
errno = ENOENT ;
602
790
if (file && hide && set_hidden_flag (wfilename , 1 ))
603
791
warning ("could not mark '%s' as hidden." , filename );
792
+ #ifdef DEBUG_FILE_LOCKS
793
+ if (file )
794
+ alloc_file_lock_backtrace (fileno (file ), filename );
795
+ #endif
604
796
return file ;
605
797
}
606
798
@@ -609,6 +801,9 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
609
801
int hide = needs_hiding (filename );
610
802
FILE * file ;
611
803
wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
804
+ #ifdef DEBUG_FILE_LOCKS
805
+ int oldfd = fileno (stream );
806
+ #endif
612
807
if (filename && !strcmp (filename , "/dev/null" ))
613
808
filename = "nul" ;
614
809
if (xutftowcs_long_path (wfilename , filename ) < 0 ||
@@ -621,9 +816,37 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
621
816
file = _wfreopen (wfilename , wotype , stream );
622
817
if (file && hide && set_hidden_flag (wfilename , 1 ))
623
818
warning ("could not mark '%s' as hidden." , filename );
819
+ #ifdef DEBUG_FILE_LOCKS
820
+ if (file ) {
821
+ remove_file_lock_backtrace (oldfd );
822
+ alloc_file_lock_backtrace (fileno (file ), filename );
823
+ }
824
+ #endif
624
825
return file ;
625
826
}
626
827
828
+ #ifdef DEBUG_FILE_LOCKS
829
+ #undef close
830
+ int mingw_close (int fd )
831
+ {
832
+ int ret = close (fd );
833
+ if (!ret )
834
+ remove_file_lock_backtrace (fd );
835
+ return ret ;
836
+ }
837
+ #define close mingw_close
838
+
839
+ #undef fclose
840
+ int mingw_fclose (FILE * stream )
841
+ {
842
+ int fd = fileno (stream ), ret = fclose (stream );
843
+ if (!ret )
844
+ remove_file_lock_backtrace (fd );
845
+ return ret ;
846
+ }
847
+ #define fclose mingw_fclose
848
+ #endif
849
+
627
850
#undef fflush
628
851
int mingw_fflush (FILE * stream )
629
852
{
@@ -2438,6 +2661,26 @@ int mingw_rename(const char *pold, const char *pnew)
2438
2661
SetFileAttributesW (wpnew , attrs & ~FILE_ATTRIBUTE_READONLY ))
2439
2662
goto repeat ;
2440
2663
}
2664
+ #ifdef DEBUG_FILE_LOCKS
2665
+ {
2666
+ struct file_lock_backtrace * p = file_lock_lookup (pnew );
2667
+ const char * which = "target" ;
2668
+ if (!p ) {
2669
+ p = file_lock_lookup (pold );
2670
+ which = "source" ;
2671
+ }
2672
+ if (p ) {
2673
+ struct strbuf buf = STRBUF_INIT ;
2674
+ strbuf_addf (& buf , "the file '%s' wants to be "
2675
+ "renamed to '%s' here:\n" , pold , pnew );
2676
+ current_backtrace (& buf );
2677
+ strbuf_addf (& buf , "\nBut the %s is still open:\n" ,
2678
+ which );
2679
+ file_lock_backtrace (p , & buf );
2680
+ die ("%s\n" , buf .buf );
2681
+ }
2682
+ }
2683
+ #endif
2441
2684
if (retry_ask_yes_no (& tries , "Rename from '%s' to '%s' failed. "
2442
2685
"Should I try again?" , pold , pnew ))
2443
2686
goto repeat ;
@@ -3313,6 +3556,9 @@ int msc_startup(int argc, wchar_t **w_argv, wchar_t **w_env)
3313
3556
fsync_object_files = 1 ;
3314
3557
maybe_redirect_std_handles ();
3315
3558
adjust_symlink_flags ();
3559
+ #ifdef DEBUG_FILE_LOCKS
3560
+ initialize_file_lock_map ();
3561
+ #endif
3316
3562
3317
3563
/* determine size of argv conversion buffer */
3318
3564
maxlen = wcslen (_wpgmptr );
@@ -3381,6 +3627,9 @@ void mingw_startup(void)
3381
3627
fsync_object_files = 1 ;
3382
3628
maybe_redirect_std_handles ();
3383
3629
adjust_symlink_flags ();
3630
+ #ifdef DEBUG_FILE_LOCKS
3631
+ initialize_file_lock_map ();
3632
+ #endif
3384
3633
3385
3634
/* get wide char arguments and environment */
3386
3635
si .newmode = 0 ;
0 commit comments