diff options
author | Joanna Wang <jojwang@google.com> | 2022-12-09 17:49:07 -0500 |
---|---|---|
committer | Joanna Wang <jojwang@google.com> | 2022-12-09 22:49:31 +0000 |
commit | 0324e43242ff078dfce70e80ce8e00d394f5ccdb (patch) | |
tree | d9a08362186827c10c8fbe01162692c9ac1a575a /repo_trace.py | |
parent | 8d25584f6987bbef81277996203f0967c4d8b4da (diff) | |
download | git-repo-0324e43242ff078dfce70e80ce8e00d394f5ccdb.tar.gz |
repo_trace: Avoid race conditions with trace_file updating.
Change-Id: I0bc1bb3c8f60465dc6bee5081688a9f163dd8cf8
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/354515
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Joanna Wang <jojwang@google.com>
Diffstat (limited to 'repo_trace.py')
-rw-r--r-- | repo_trace.py | 55 |
1 files changed, 37 insertions, 18 deletions
diff --git a/repo_trace.py b/repo_trace.py index d79408d9..1ba86c79 100644 --- a/repo_trace.py +++ b/repo_trace.py | |||
@@ -23,6 +23,7 @@ To also include trace outputs in stderr do `repo --trace_to_stderr ...` | |||
23 | import sys | 23 | import sys |
24 | import os | 24 | import os |
25 | import time | 25 | import time |
26 | import tempfile | ||
26 | from contextlib import ContextDecorator | 27 | from contextlib import ContextDecorator |
27 | 28 | ||
28 | import platform_utils | 29 | import platform_utils |
@@ -35,34 +36,40 @@ _TRACE = os.environ.get(REPO_TRACE) != '0' | |||
35 | _TRACE_TO_STDERR = False | 36 | _TRACE_TO_STDERR = False |
36 | _TRACE_FILE = None | 37 | _TRACE_FILE = None |
37 | _TRACE_FILE_NAME = 'TRACE_FILE' | 38 | _TRACE_FILE_NAME = 'TRACE_FILE' |
38 | _MAX_SIZE = 70 # in mb | 39 | _MAX_SIZE = 70 # in MiB |
39 | _NEW_COMMAND_SEP = '+++++++++++++++NEW COMMAND+++++++++++++++++++' | 40 | _NEW_COMMAND_SEP = '+++++++++++++++NEW COMMAND+++++++++++++++++++' |
40 | 41 | ||
41 | 42 | ||
42 | def IsTraceToStderr(): | 43 | def IsTraceToStderr(): |
44 | """Whether traces are written to stderr.""" | ||
43 | return _TRACE_TO_STDERR | 45 | return _TRACE_TO_STDERR |
44 | 46 | ||
45 | 47 | ||
46 | def IsTrace(): | 48 | def IsTrace(): |
49 | """Whether tracing is enabled.""" | ||
47 | return _TRACE | 50 | return _TRACE |
48 | 51 | ||
49 | 52 | ||
50 | def SetTraceToStderr(): | 53 | def SetTraceToStderr(): |
54 | """Enables tracing logging to stderr.""" | ||
51 | global _TRACE_TO_STDERR | 55 | global _TRACE_TO_STDERR |
52 | _TRACE_TO_STDERR = True | 56 | _TRACE_TO_STDERR = True |
53 | 57 | ||
54 | 58 | ||
55 | def SetTrace(): | 59 | def SetTrace(): |
60 | """Enables tracing.""" | ||
56 | global _TRACE | 61 | global _TRACE |
57 | _TRACE = True | 62 | _TRACE = True |
58 | 63 | ||
59 | 64 | ||
60 | def _SetTraceFile(quiet): | 65 | def _SetTraceFile(quiet): |
66 | """Sets the trace file location.""" | ||
61 | global _TRACE_FILE | 67 | global _TRACE_FILE |
62 | _TRACE_FILE = _GetTraceFile(quiet) | 68 | _TRACE_FILE = _GetTraceFile(quiet) |
63 | 69 | ||
64 | 70 | ||
65 | class Trace(ContextDecorator): | 71 | class Trace(ContextDecorator): |
72 | """Used to capture and save git traces.""" | ||
66 | 73 | ||
67 | def _time(self): | 74 | def _time(self): |
68 | """Generate nanoseconds of time in a py3.6 safe way""" | 75 | """Generate nanoseconds of time in a py3.6 safe way""" |
@@ -128,20 +135,32 @@ def _GetTraceFile(quiet): | |||
128 | 135 | ||
129 | 136 | ||
130 | def _ClearOldTraces(): | 137 | def _ClearOldTraces(): |
131 | """Clear the oldest commands if trace file is too big. | 138 | """Clear the oldest commands if trace file is too big.""" |
132 | 139 | try: | |
133 | Note: If the trace file contains output from two `repo` | 140 | with open(_TRACE_FILE, 'r', errors='ignore') as f: |
134 | commands that were running at the same time, this | 141 | if os.path.getsize(f.name) / (1024 * 1024) <= _MAX_SIZE: |
135 | will not work precisely. | 142 | return |
136 | """ | 143 | trace_lines = f.readlines() |
137 | if os.path.isfile(_TRACE_FILE): | 144 | except FileNotFoundError: |
138 | while os.path.getsize(_TRACE_FILE) / (1024 * 1024) > _MAX_SIZE: | 145 | return |
139 | temp_file = _TRACE_FILE + '.tmp' | 146 | |
140 | with open(_TRACE_FILE, 'r', errors='ignore') as fin: | 147 | while sum(len(x) for x in trace_lines) / (1024 * 1024) > _MAX_SIZE: |
141 | with open(temp_file, 'w') as tf: | 148 | for i, line in enumerate(trace_lines): |
142 | trace_lines = fin.readlines() | 149 | if 'END:' in line and _NEW_COMMAND_SEP in line: |
143 | for i, l in enumerate(trace_lines): | 150 | trace_lines = trace_lines[i + 1:] |
144 | if 'END:' in l and _NEW_COMMAND_SEP in l: | 151 | break |
145 | tf.writelines(trace_lines[i + 1:]) | 152 | else: |
146 | break | 153 | # The last chunk is bigger than _MAX_SIZE, so just throw everything away. |
147 | platform_utils.rename(temp_file, _TRACE_FILE) | 154 | trace_lines = [] |
155 | |||
156 | while trace_lines and trace_lines[-1] == '\n': | ||
157 | trace_lines = trace_lines[:-1] | ||
158 | # Write to a temporary file with a unique name in the same filesystem | ||
159 | # before replacing the original trace file. | ||
160 | temp_dir, temp_prefix = os.path.split(_TRACE_FILE) | ||
161 | with tempfile.NamedTemporaryFile('w', | ||
162 | dir=temp_dir, | ||
163 | prefix=temp_prefix, | ||
164 | delete=False) as f: | ||
165 | f.writelines(trace_lines) | ||
166 | platform_utils.rename(f.name, _TRACE_FILE) | ||