88 */
99#define MAX_AUTO_ATTEMPTS 10
1010
11+ /*
12+ * Sentinel file used to detect when we should discard new traces to avoid
13+ * writing too many trace files to a directory.
14+ */
15+ #define DISCARD_SENTINEL_NAME "git-trace2-discard"
16+
17+ /*
18+ * When set to zero, disables directory file count checks. Otherwise, controls
19+ * how many files we can write to a directory before entering discard mode.
20+ * This can be overridden via the TR2_SYSENV_MAX_FILES setting.
21+ */
22+ static int tr2env_max_files = 0 ;
23+
1124static int tr2_dst_want_warning (void )
1225{
1326 static int tr2env_dst_debug = -1 ;
@@ -32,9 +45,75 @@ void tr2_dst_trace_disable(struct tr2_dst *dst)
3245 dst -> need_close = 0 ;
3346}
3447
48+ /*
49+ * Check to make sure we're not overloading the target directory with too many
50+ * files. First get the threshold (if present) from the config or envvar. If
51+ * it's zero or unset, disable this check. Next check for the presence of a
52+ * sentinel file, then check file count.
53+ *
54+ * Returns 0 if tracing should proceed as normal. Returns 1 if the sentinel file
55+ * already exists, which means tracing should be disabled. Returns -1 if there
56+ * are too many files but there was no sentinel file, which means we have
57+ * created and should write traces to the sentinel file.
58+ *
59+ * We expect that some trace processing system is gradually collecting files
60+ * from the target directory; after it removes the sentinel file we'll start
61+ * writing traces again.
62+ */
63+ static int tr2_dst_too_many_files (struct tr2_dst * dst , const char * tgt_prefix )
64+ {
65+ int file_count = 0 , max_files = 0 , ret = 0 ;
66+ const char * max_files_var ;
67+ DIR * dirp ;
68+ struct strbuf path = STRBUF_INIT , sentinel_path = STRBUF_INIT ;
69+ struct stat statbuf ;
70+
71+ /* Get the config or envvar and decide if we should continue this check */
72+ max_files_var = tr2_sysenv_get (TR2_SYSENV_MAX_FILES );
73+ if (max_files_var && * max_files_var && ((max_files = atoi (max_files_var )) >= 0 ))
74+ tr2env_max_files = max_files ;
75+
76+ if (!tr2env_max_files ) {
77+ ret = 0 ;
78+ goto cleanup ;
79+ }
80+
81+ strbuf_addstr (& path , tgt_prefix );
82+ if (!is_dir_sep (path .buf [path .len - 1 ])) {
83+ strbuf_addch (& path , '/' );
84+ }
85+
86+ /* check sentinel */
87+ strbuf_addbuf (& sentinel_path , & path );
88+ strbuf_addstr (& sentinel_path , DISCARD_SENTINEL_NAME );
89+ if (!stat (sentinel_path .buf , & statbuf )) {
90+ ret = 1 ;
91+ goto cleanup ;
92+ }
93+
94+ /* check file count */
95+ dirp = opendir (path .buf );
96+ while (file_count < tr2env_max_files && dirp && readdir (dirp ))
97+ file_count ++ ;
98+ if (dirp )
99+ closedir (dirp );
100+
101+ if (file_count >= tr2env_max_files ) {
102+ dst -> too_many_files = 1 ;
103+ dst -> fd = open (sentinel_path .buf , O_WRONLY | O_CREAT | O_EXCL , 0666 );
104+ ret = -1 ;
105+ goto cleanup ;
106+ }
107+
108+ cleanup :
109+ strbuf_release (& path );
110+ strbuf_release (& sentinel_path );
111+ return ret ;
112+ }
113+
35114static int tr2_dst_try_auto_path (struct tr2_dst * dst , const char * tgt_prefix )
36115{
37- int fd ;
116+ int too_many_files ;
38117 const char * last_slash , * sid = tr2_sid_get ();
39118 struct strbuf path = STRBUF_INIT ;
40119 size_t base_path_len ;
@@ -50,18 +129,29 @@ static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix)
50129 strbuf_addstr (& path , sid );
51130 base_path_len = path .len ;
52131
53- for (attempt_count = 0 ; attempt_count < MAX_AUTO_ATTEMPTS ; attempt_count ++ ) {
54- if (attempt_count > 0 ) {
55- strbuf_setlen (& path , base_path_len );
56- strbuf_addf (& path , ".%d" , attempt_count );
132+ too_many_files = tr2_dst_too_many_files (dst , tgt_prefix );
133+ if (!too_many_files ) {
134+ for (attempt_count = 0 ; attempt_count < MAX_AUTO_ATTEMPTS ; attempt_count ++ ) {
135+ if (attempt_count > 0 ) {
136+ strbuf_setlen (& path , base_path_len );
137+ strbuf_addf (& path , ".%d" , attempt_count );
138+ }
139+
140+ dst -> fd = open (path .buf , O_WRONLY | O_CREAT | O_EXCL , 0666 );
141+ if (dst -> fd != -1 )
142+ break ;
57143 }
58-
59- fd = open (path .buf , O_WRONLY | O_CREAT | O_EXCL , 0666 );
60- if (fd != -1 )
61- break ;
144+ } else if (too_many_files == 1 ) {
145+ strbuf_release (& path );
146+ if (tr2_dst_want_warning ())
147+ warning ("trace2: not opening %s trace file due to too "
148+ "many files in target directory %s" ,
149+ tr2_sysenv_display_name (dst -> sysenv_var ),
150+ tgt_prefix );
151+ return 0 ;
62152 }
63153
64- if (fd == -1 ) {
154+ if (dst -> fd == -1 ) {
65155 if (tr2_dst_want_warning ())
66156 warning ("trace2: could not open '%.*s' for '%s' tracing: %s" ,
67157 (int ) base_path_len , path .buf ,
@@ -75,7 +165,6 @@ static int tr2_dst_try_auto_path(struct tr2_dst *dst, const char *tgt_prefix)
75165
76166 strbuf_release (& path );
77167
78- dst -> fd = fd ;
79168 dst -> need_close = 1 ;
80169 dst -> initialized = 1 ;
81170
0 commit comments