Line | Count | Source |
1 | | #include "git-compat-util.h" |
2 | | #include "hex.h" |
3 | | #include "refs-internal.h" |
4 | | #include "trace.h" |
5 | | |
6 | | static struct trace_key trace_refs = TRACE_KEY_INIT(REFS); |
7 | | |
8 | | struct debug_ref_store { |
9 | | struct ref_store base; |
10 | | struct ref_store *refs; |
11 | | }; |
12 | | |
13 | | extern struct ref_storage_be refs_be_debug; |
14 | | |
15 | | struct ref_store *maybe_debug_wrap_ref_store(const char *gitdir, struct ref_store *store) |
16 | 0 | { |
17 | 0 | struct debug_ref_store *res; |
18 | 0 | struct ref_storage_be *be_copy; |
19 | |
|
20 | 0 | if (!trace_want(&trace_refs)) { |
21 | 0 | return store; |
22 | 0 | } |
23 | 0 | res = xmalloc(sizeof(struct debug_ref_store)); |
24 | 0 | be_copy = xmalloc(sizeof(*be_copy)); |
25 | 0 | *be_copy = refs_be_debug; |
26 | | /* we never deallocate backends, so safe to copy the pointer. */ |
27 | 0 | be_copy->name = store->be->name; |
28 | 0 | trace_printf_key(&trace_refs, "ref_store for %s\n", gitdir); |
29 | 0 | res->refs = store; |
30 | 0 | base_ref_store_init((struct ref_store *)res, store->repo, gitdir, |
31 | 0 | be_copy); |
32 | 0 | return (struct ref_store *)res; |
33 | 0 | } |
34 | | |
35 | | static void debug_release(struct ref_store *refs) |
36 | 0 | { |
37 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
38 | 0 | drefs->refs->be->release(drefs->refs); |
39 | 0 | trace_printf_key(&trace_refs, "release\n"); |
40 | 0 | } |
41 | | |
42 | | static int debug_create_on_disk(struct ref_store *refs, int flags, struct strbuf *err) |
43 | 0 | { |
44 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
45 | 0 | int res = drefs->refs->be->create_on_disk(drefs->refs, flags, err); |
46 | 0 | trace_printf_key(&trace_refs, "create_on_disk: %d\n", res); |
47 | 0 | return res; |
48 | 0 | } |
49 | | |
50 | | static int debug_remove_on_disk(struct ref_store *refs, struct strbuf *err) |
51 | 0 | { |
52 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
53 | 0 | int res = drefs->refs->be->remove_on_disk(drefs->refs, err); |
54 | 0 | trace_printf_key(&trace_refs, "remove_on_disk: %d\n", res); |
55 | 0 | return res; |
56 | 0 | } |
57 | | |
58 | | static int debug_transaction_prepare(struct ref_store *refs, |
59 | | struct ref_transaction *transaction, |
60 | | struct strbuf *err) |
61 | 0 | { |
62 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
63 | 0 | int res; |
64 | 0 | transaction->ref_store = drefs->refs; |
65 | 0 | res = drefs->refs->be->transaction_prepare(drefs->refs, transaction, |
66 | 0 | err); |
67 | 0 | trace_printf_key(&trace_refs, "transaction_prepare: %d \"%s\"\n", res, |
68 | 0 | err->buf); |
69 | 0 | return res; |
70 | 0 | } |
71 | | |
72 | | static void print_update(int i, const char *refname, |
73 | | const struct object_id *old_oid, |
74 | | const struct object_id *new_oid, unsigned int flags, |
75 | | unsigned int type, const char *msg) |
76 | 0 | { |
77 | 0 | char o[GIT_MAX_HEXSZ + 1] = "null"; |
78 | 0 | char n[GIT_MAX_HEXSZ + 1] = "null"; |
79 | 0 | if (old_oid) |
80 | 0 | oid_to_hex_r(o, old_oid); |
81 | 0 | if (new_oid) |
82 | 0 | oid_to_hex_r(n, new_oid); |
83 | |
|
84 | 0 | type &= 0xf; /* see refs.h REF_* */ |
85 | 0 | flags &= REF_HAVE_NEW | REF_HAVE_OLD | REF_NO_DEREF | |
86 | 0 | REF_FORCE_CREATE_REFLOG; |
87 | 0 | trace_printf_key(&trace_refs, "%d: %s %s -> %s (F=0x%x, T=0x%x) \"%s\"\n", i, refname, |
88 | 0 | o, n, flags, type, msg); |
89 | 0 | } |
90 | | |
91 | | static void print_transaction(struct ref_transaction *transaction) |
92 | 0 | { |
93 | 0 | trace_printf_key(&trace_refs, "transaction {\n"); |
94 | 0 | for (size_t i = 0; i < transaction->nr; i++) { |
95 | 0 | struct ref_update *u = transaction->updates[i]; |
96 | 0 | print_update(i, u->refname, &u->old_oid, &u->new_oid, u->flags, |
97 | 0 | u->type, u->msg); |
98 | 0 | } |
99 | 0 | trace_printf_key(&trace_refs, "}\n"); |
100 | 0 | } |
101 | | |
102 | | static int debug_transaction_finish(struct ref_store *refs, |
103 | | struct ref_transaction *transaction, |
104 | | struct strbuf *err) |
105 | 0 | { |
106 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
107 | 0 | int res; |
108 | 0 | transaction->ref_store = drefs->refs; |
109 | 0 | res = drefs->refs->be->transaction_finish(drefs->refs, transaction, |
110 | 0 | err); |
111 | 0 | print_transaction(transaction); |
112 | 0 | trace_printf_key(&trace_refs, "finish: %d\n", res); |
113 | 0 | return res; |
114 | 0 | } |
115 | | |
116 | | static int debug_transaction_abort(struct ref_store *refs, |
117 | | struct ref_transaction *transaction, |
118 | | struct strbuf *err) |
119 | 0 | { |
120 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)refs; |
121 | 0 | int res; |
122 | 0 | transaction->ref_store = drefs->refs; |
123 | 0 | res = drefs->refs->be->transaction_abort(drefs->refs, transaction, err); |
124 | 0 | return res; |
125 | 0 | } |
126 | | |
127 | | static int debug_optimize(struct ref_store *ref_store, struct refs_optimize_opts *opts) |
128 | 0 | { |
129 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
130 | 0 | int res = drefs->refs->be->optimize(drefs->refs, opts); |
131 | 0 | trace_printf_key(&trace_refs, "optimize: %d\n", res); |
132 | 0 | return res; |
133 | 0 | } |
134 | | |
135 | | static int debug_optimize_required(struct ref_store *ref_store, |
136 | | struct refs_optimize_opts *opts, |
137 | | bool *required) |
138 | 0 | { |
139 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
140 | 0 | int res = drefs->refs->be->optimize_required(drefs->refs, opts, required); |
141 | 0 | trace_printf_key(&trace_refs, "optimize_required: %s, res: %d\n", |
142 | 0 | *required ? "yes" : "no", res); |
143 | 0 | return res; |
144 | 0 | } |
145 | | |
146 | | static int debug_rename_ref(struct ref_store *ref_store, const char *oldref, |
147 | | const char *newref, const char *logmsg) |
148 | 0 | { |
149 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
150 | 0 | int res = drefs->refs->be->rename_ref(drefs->refs, oldref, newref, |
151 | 0 | logmsg); |
152 | 0 | trace_printf_key(&trace_refs, "rename_ref: %s -> %s \"%s\": %d\n", oldref, newref, |
153 | 0 | logmsg, res); |
154 | 0 | return res; |
155 | 0 | } |
156 | | |
157 | | static int debug_copy_ref(struct ref_store *ref_store, const char *oldref, |
158 | | const char *newref, const char *logmsg) |
159 | 0 | { |
160 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
161 | 0 | int res = |
162 | 0 | drefs->refs->be->copy_ref(drefs->refs, oldref, newref, logmsg); |
163 | 0 | trace_printf_key(&trace_refs, "copy_ref: %s -> %s \"%s\": %d\n", oldref, newref, |
164 | 0 | logmsg, res); |
165 | 0 | return res; |
166 | 0 | } |
167 | | |
168 | | struct debug_ref_iterator { |
169 | | struct ref_iterator base; |
170 | | struct ref_iterator *iter; |
171 | | }; |
172 | | |
173 | | static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator) |
174 | 0 | { |
175 | 0 | struct debug_ref_iterator *diter = |
176 | 0 | (struct debug_ref_iterator *)ref_iterator; |
177 | 0 | int res = diter->iter->vtable->advance(diter->iter); |
178 | 0 | if (res) |
179 | 0 | trace_printf_key(&trace_refs, "iterator_advance: (%d)\n", res); |
180 | 0 | else |
181 | 0 | trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n", |
182 | 0 | diter->iter->ref.name); |
183 | |
|
184 | 0 | diter->base.ref = diter->iter->ref; |
185 | 0 | return res; |
186 | 0 | } |
187 | | |
188 | | static int debug_ref_iterator_seek(struct ref_iterator *ref_iterator, |
189 | | const char *refname, unsigned int flags) |
190 | 0 | { |
191 | 0 | struct debug_ref_iterator *diter = |
192 | 0 | (struct debug_ref_iterator *)ref_iterator; |
193 | 0 | int res = diter->iter->vtable->seek(diter->iter, refname, flags); |
194 | 0 | trace_printf_key(&trace_refs, "iterator_seek: %s flags: %d: %d\n", |
195 | 0 | refname ? refname : "", flags, res); |
196 | 0 | return res; |
197 | 0 | } |
198 | | |
199 | | static void debug_ref_iterator_release(struct ref_iterator *ref_iterator) |
200 | 0 | { |
201 | 0 | struct debug_ref_iterator *diter = |
202 | 0 | (struct debug_ref_iterator *)ref_iterator; |
203 | 0 | diter->iter->vtable->release(diter->iter); |
204 | 0 | trace_printf_key(&trace_refs, "iterator_abort\n"); |
205 | 0 | } |
206 | | |
207 | | static struct ref_iterator_vtable debug_ref_iterator_vtable = { |
208 | | .advance = debug_ref_iterator_advance, |
209 | | .seek = debug_ref_iterator_seek, |
210 | | .release = debug_ref_iterator_release, |
211 | | }; |
212 | | |
213 | | static struct ref_iterator * |
214 | | debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, |
215 | | const char **exclude_patterns, unsigned int flags) |
216 | 0 | { |
217 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
218 | 0 | struct ref_iterator *res = |
219 | 0 | drefs->refs->be->iterator_begin(drefs->refs, prefix, |
220 | 0 | exclude_patterns, flags); |
221 | 0 | struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter)); |
222 | 0 | base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable); |
223 | 0 | diter->iter = res; |
224 | 0 | trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n", |
225 | 0 | prefix, flags); |
226 | 0 | return &diter->base; |
227 | 0 | } |
228 | | |
229 | | static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname, |
230 | | struct object_id *oid, struct strbuf *referent, |
231 | | unsigned int *type, int *failure_errno) |
232 | 0 | { |
233 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
234 | 0 | int res = 0; |
235 | |
|
236 | 0 | oidcpy(oid, null_oid(ref_store->repo->hash_algo)); |
237 | 0 | res = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent, |
238 | 0 | type, failure_errno); |
239 | |
|
240 | 0 | if (res == 0) { |
241 | 0 | trace_printf_key(&trace_refs, "read_raw_ref: %s: %s (=> %s) type %x: %d\n", |
242 | 0 | refname, oid_to_hex(oid), referent->buf, *type, res); |
243 | 0 | } else { |
244 | 0 | trace_printf_key(&trace_refs, |
245 | 0 | "read_raw_ref: %s: %d (errno %d)\n", refname, |
246 | 0 | res, *failure_errno); |
247 | 0 | } |
248 | 0 | return res; |
249 | 0 | } |
250 | | |
251 | | static int debug_read_symbolic_ref(struct ref_store *ref_store, const char *refname, |
252 | | struct strbuf *referent) |
253 | 0 | { |
254 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
255 | 0 | struct ref_store *refs = drefs->refs; |
256 | 0 | int res; |
257 | |
|
258 | 0 | res = refs->be->read_symbolic_ref(refs, refname, referent); |
259 | 0 | if (!res) |
260 | 0 | trace_printf_key(&trace_refs, "read_symbolic_ref: %s: (%s)\n", |
261 | 0 | refname, referent->buf); |
262 | 0 | else |
263 | 0 | trace_printf_key(&trace_refs, |
264 | 0 | "read_symbolic_ref: %s: %d\n", refname, res); |
265 | 0 | return res; |
266 | |
|
267 | 0 | } |
268 | | |
269 | | static struct ref_iterator * |
270 | | debug_reflog_iterator_begin(struct ref_store *ref_store) |
271 | 0 | { |
272 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
273 | 0 | struct ref_iterator *res = |
274 | 0 | drefs->refs->be->reflog_iterator_begin(drefs->refs); |
275 | 0 | trace_printf_key(&trace_refs, "for_each_reflog_iterator_begin\n"); |
276 | 0 | return res; |
277 | 0 | } |
278 | | |
279 | | struct debug_reflog { |
280 | | const char *refname; |
281 | | each_reflog_ent_fn *fn; |
282 | | void *cb_data; |
283 | | }; |
284 | | |
285 | | static int debug_print_reflog_ent(const char *refname, |
286 | | struct object_id *old_oid, |
287 | | struct object_id *new_oid, |
288 | | const char *committer, timestamp_t timestamp, |
289 | | int tz, const char *msg, void *cb_data) |
290 | 0 | { |
291 | 0 | struct debug_reflog *dbg = (struct debug_reflog *)cb_data; |
292 | 0 | int ret; |
293 | 0 | char o[GIT_MAX_HEXSZ + 1] = "null"; |
294 | 0 | char n[GIT_MAX_HEXSZ + 1] = "null"; |
295 | 0 | char *msgend = strchrnul(msg, '\n'); |
296 | 0 | if (old_oid) |
297 | 0 | oid_to_hex_r(o, old_oid); |
298 | 0 | if (new_oid) |
299 | 0 | oid_to_hex_r(n, new_oid); |
300 | |
|
301 | 0 | ret = dbg->fn(refname, old_oid, new_oid, committer, timestamp, tz, msg, |
302 | 0 | dbg->cb_data); |
303 | 0 | trace_printf_key(&trace_refs, |
304 | 0 | "reflog_ent %s (ret %d): %s -> %s, %s %ld \"%.*s\"\n", |
305 | 0 | dbg->refname, ret, o, n, committer, |
306 | 0 | (long int)timestamp, (int)(msgend - msg), msg); |
307 | 0 | return ret; |
308 | 0 | } |
309 | | |
310 | | static int debug_for_each_reflog_ent(struct ref_store *ref_store, |
311 | | const char *refname, each_reflog_ent_fn fn, |
312 | | void *cb_data) |
313 | 0 | { |
314 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
315 | 0 | struct debug_reflog dbg = { |
316 | 0 | .refname = refname, |
317 | 0 | .fn = fn, |
318 | 0 | .cb_data = cb_data, |
319 | 0 | }; |
320 | |
|
321 | 0 | int res = drefs->refs->be->for_each_reflog_ent( |
322 | 0 | drefs->refs, refname, &debug_print_reflog_ent, &dbg); |
323 | 0 | trace_printf_key(&trace_refs, "for_each_reflog: %s: %d\n", refname, res); |
324 | 0 | return res; |
325 | 0 | } |
326 | | |
327 | | static int debug_for_each_reflog_ent_reverse(struct ref_store *ref_store, |
328 | | const char *refname, |
329 | | each_reflog_ent_fn fn, |
330 | | void *cb_data) |
331 | 0 | { |
332 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
333 | 0 | struct debug_reflog dbg = { |
334 | 0 | .refname = refname, |
335 | 0 | .fn = fn, |
336 | 0 | .cb_data = cb_data, |
337 | 0 | }; |
338 | 0 | int res = drefs->refs->be->for_each_reflog_ent_reverse( |
339 | 0 | drefs->refs, refname, &debug_print_reflog_ent, &dbg); |
340 | 0 | trace_printf_key(&trace_refs, "for_each_reflog_reverse: %s: %d\n", refname, res); |
341 | 0 | return res; |
342 | 0 | } |
343 | | |
344 | | static int debug_reflog_exists(struct ref_store *ref_store, const char *refname) |
345 | 0 | { |
346 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
347 | 0 | int res = drefs->refs->be->reflog_exists(drefs->refs, refname); |
348 | 0 | trace_printf_key(&trace_refs, "reflog_exists: %s: %d\n", refname, res); |
349 | 0 | return res; |
350 | 0 | } |
351 | | |
352 | | static int debug_create_reflog(struct ref_store *ref_store, const char *refname, |
353 | | struct strbuf *err) |
354 | 0 | { |
355 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
356 | 0 | int res = drefs->refs->be->create_reflog(drefs->refs, refname, err); |
357 | 0 | trace_printf_key(&trace_refs, "create_reflog: %s: %d\n", refname, res); |
358 | 0 | return res; |
359 | 0 | } |
360 | | |
361 | | static int debug_delete_reflog(struct ref_store *ref_store, const char *refname) |
362 | 0 | { |
363 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
364 | 0 | int res = drefs->refs->be->delete_reflog(drefs->refs, refname); |
365 | 0 | trace_printf_key(&trace_refs, "delete_reflog: %s: %d\n", refname, res); |
366 | 0 | return res; |
367 | 0 | } |
368 | | |
369 | | struct debug_reflog_expiry_should_prune { |
370 | | reflog_expiry_prepare_fn *prepare; |
371 | | reflog_expiry_should_prune_fn *should_prune; |
372 | | reflog_expiry_cleanup_fn *cleanup; |
373 | | void *cb_data; |
374 | | }; |
375 | | |
376 | | static void debug_reflog_expiry_prepare(const char *refname, |
377 | | const struct object_id *oid, |
378 | | void *cb_data) |
379 | 0 | { |
380 | 0 | struct debug_reflog_expiry_should_prune *prune = cb_data; |
381 | 0 | trace_printf_key(&trace_refs, "reflog_expire_prepare: %s\n", refname); |
382 | 0 | prune->prepare(refname, oid, prune->cb_data); |
383 | 0 | } |
384 | | |
385 | | static int debug_reflog_expiry_should_prune_fn(struct object_id *ooid, |
386 | | struct object_id *noid, |
387 | | const char *email, |
388 | | timestamp_t timestamp, int tz, |
389 | 0 | const char *message, void *cb_data) { |
390 | 0 | struct debug_reflog_expiry_should_prune *prune = cb_data; |
391 | |
|
392 | 0 | int result = prune->should_prune(ooid, noid, email, timestamp, tz, message, prune->cb_data); |
393 | 0 | trace_printf_key(&trace_refs, "reflog_expire_should_prune: %s %ld: %d\n", message, (long int) timestamp, result); |
394 | 0 | return result; |
395 | 0 | } |
396 | | |
397 | | static void debug_reflog_expiry_cleanup(void *cb_data) |
398 | 0 | { |
399 | 0 | struct debug_reflog_expiry_should_prune *prune = cb_data; |
400 | 0 | prune->cleanup(prune->cb_data); |
401 | 0 | } |
402 | | |
403 | | static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, |
404 | | unsigned int flags, |
405 | | reflog_expiry_prepare_fn prepare_fn, |
406 | | reflog_expiry_should_prune_fn should_prune_fn, |
407 | | reflog_expiry_cleanup_fn cleanup_fn, |
408 | | void *policy_cb_data) |
409 | 0 | { |
410 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
411 | 0 | struct debug_reflog_expiry_should_prune prune = { |
412 | 0 | .prepare = prepare_fn, |
413 | 0 | .cleanup = cleanup_fn, |
414 | 0 | .should_prune = should_prune_fn, |
415 | 0 | .cb_data = policy_cb_data, |
416 | 0 | }; |
417 | 0 | int res = drefs->refs->be->reflog_expire(drefs->refs, refname, |
418 | 0 | flags, &debug_reflog_expiry_prepare, |
419 | 0 | &debug_reflog_expiry_should_prune_fn, |
420 | 0 | &debug_reflog_expiry_cleanup, |
421 | 0 | &prune); |
422 | 0 | trace_printf_key(&trace_refs, "reflog_expire: %s: %d\n", refname, res); |
423 | 0 | return res; |
424 | 0 | } |
425 | | |
426 | | static int debug_fsck(struct ref_store *ref_store, |
427 | | struct fsck_options *o, |
428 | | struct worktree *wt) |
429 | 0 | { |
430 | 0 | struct debug_ref_store *drefs = (struct debug_ref_store *)ref_store; |
431 | 0 | int res = drefs->refs->be->fsck(drefs->refs, o, wt); |
432 | 0 | trace_printf_key(&trace_refs, "fsck: %d\n", res); |
433 | 0 | return res; |
434 | 0 | } |
435 | | |
436 | | struct ref_storage_be refs_be_debug = { |
437 | | .name = "debug", |
438 | | .init = NULL, |
439 | | .release = debug_release, |
440 | | .create_on_disk = debug_create_on_disk, |
441 | | .remove_on_disk = debug_remove_on_disk, |
442 | | |
443 | | /* |
444 | | * None of these should be NULL. If the "files" backend (in |
445 | | * "struct ref_storage_be refs_be_files" in files-backend.c) |
446 | | * has a function we should also have a wrapper for it here. |
447 | | * Test the output with "GIT_TRACE_REFS=1". |
448 | | */ |
449 | | .transaction_prepare = debug_transaction_prepare, |
450 | | .transaction_finish = debug_transaction_finish, |
451 | | .transaction_abort = debug_transaction_abort, |
452 | | |
453 | | .optimize = debug_optimize, |
454 | | .optimize_required = debug_optimize_required, |
455 | | |
456 | | .rename_ref = debug_rename_ref, |
457 | | .copy_ref = debug_copy_ref, |
458 | | |
459 | | .iterator_begin = debug_ref_iterator_begin, |
460 | | .read_raw_ref = debug_read_raw_ref, |
461 | | .read_symbolic_ref = debug_read_symbolic_ref, |
462 | | |
463 | | .reflog_iterator_begin = debug_reflog_iterator_begin, |
464 | | .for_each_reflog_ent = debug_for_each_reflog_ent, |
465 | | .for_each_reflog_ent_reverse = debug_for_each_reflog_ent_reverse, |
466 | | .reflog_exists = debug_reflog_exists, |
467 | | .create_reflog = debug_create_reflog, |
468 | | .delete_reflog = debug_delete_reflog, |
469 | | .reflog_expire = debug_reflog_expire, |
470 | | |
471 | | .fsck = debug_fsck, |
472 | | }; |