Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 1 | # Copyright (C) 2008 The Android Open Source Project |
| 2 | # |
| 3 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | # you may not use this file except in compliance with the License. |
| 5 | # You may obtain a copy of the License at |
| 6 | # |
| 7 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | # |
| 9 | # Unless required by applicable law or agreed to in writing, software |
| 10 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | # See the License for the specific language governing permissions and |
| 13 | # limitations under the License. |
| 14 | |
Mike Frysinger | 8a11f6f | 2019-08-27 00:26:15 -0400 | [diff] [blame] | 15 | """Logic for tracing repo interactions. |
| 16 | |
| 17 | Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`. |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 18 | |
| 19 | Temporary: Tracing is always on. Set `REPO_TRACE=0` to turn off. |
| 20 | To also include trace outputs in stderr do `repo --trace_to_stderr ...` |
Mike Frysinger | 8a11f6f | 2019-08-27 00:26:15 -0400 | [diff] [blame] | 21 | """ |
| 22 | |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 23 | import sys |
| 24 | import os |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 25 | import time |
Joanna Wang | 0324e43 | 2022-12-09 17:49:07 -0500 | [diff] [blame] | 26 | import tempfile |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 27 | from contextlib import ContextDecorator |
Mike Frysinger | 8a11f6f | 2019-08-27 00:26:15 -0400 | [diff] [blame] | 28 | |
Joanna Wang | 24c6314 | 2022-11-08 18:56:52 -0500 | [diff] [blame] | 29 | import platform_utils |
| 30 | |
Mike Frysinger | 8a11f6f | 2019-08-27 00:26:15 -0400 | [diff] [blame] | 31 | # Env var to implicitly turn on tracing. |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 32 | REPO_TRACE = "REPO_TRACE" |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 33 | |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 34 | # Temporarily set tracing to always on unless user expicitly sets to 0. |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 35 | _TRACE = os.environ.get(REPO_TRACE) != "0" |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 36 | _TRACE_TO_STDERR = False |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 37 | _TRACE_FILE = None |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 38 | _TRACE_FILE_NAME = "TRACE_FILE" |
Joanna Wang | 0324e43 | 2022-12-09 17:49:07 -0500 | [diff] [blame] | 39 | _MAX_SIZE = 70 # in MiB |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 40 | _NEW_COMMAND_SEP = "+++++++++++++++NEW COMMAND+++++++++++++++++++" |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 41 | |
| 42 | |
LaMont Jones | 47020ba | 2022-11-10 00:11:51 +0000 | [diff] [blame] | 43 | def IsTraceToStderr(): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 44 | """Whether traces are written to stderr.""" |
| 45 | return _TRACE_TO_STDERR |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 46 | |
David Pursehouse | 819827a | 2020-02-12 15:20:19 +0900 | [diff] [blame] | 47 | |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 48 | def IsTrace(): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 49 | """Whether tracing is enabled.""" |
| 50 | return _TRACE |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 51 | |
David Pursehouse | 819827a | 2020-02-12 15:20:19 +0900 | [diff] [blame] | 52 | |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 53 | def SetTraceToStderr(): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 54 | """Enables tracing logging to stderr.""" |
| 55 | global _TRACE_TO_STDERR |
| 56 | _TRACE_TO_STDERR = True |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 57 | |
| 58 | |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 59 | def SetTrace(): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 60 | """Enables tracing.""" |
| 61 | global _TRACE |
| 62 | _TRACE = True |
Shawn O. Pearce | ad3193a | 2009-04-18 09:54:51 -0700 | [diff] [blame] | 63 | |
David Pursehouse | 819827a | 2020-02-12 15:20:19 +0900 | [diff] [blame] | 64 | |
LaMont Jones | ed25be5 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 65 | def _SetTraceFile(quiet): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 66 | """Sets the trace file location.""" |
| 67 | global _TRACE_FILE |
| 68 | _TRACE_FILE = _GetTraceFile(quiet) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 69 | |
| 70 | |
| 71 | class Trace(ContextDecorator): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 72 | """Used to capture and save git traces.""" |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 73 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 74 | def _time(self): |
| 75 | """Generate nanoseconds of time in a py3.6 safe way""" |
| 76 | return int(time.time() * 1e9) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 77 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 78 | def __init__(self, fmt, *args, first_trace=False, quiet=True): |
| 79 | """Initialize the object. |
LaMont Jones | ed25be5 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 80 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 81 | Args: |
| 82 | fmt: The format string for the trace. |
| 83 | *args: Arguments to pass to formatting. |
| 84 | first_trace: Whether this is the first trace of a `repo` invocation. |
| 85 | quiet: Whether to suppress notification of trace file location. |
| 86 | """ |
| 87 | if not IsTrace(): |
| 88 | return |
| 89 | self._trace_msg = fmt % args |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 90 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 91 | if not _TRACE_FILE: |
| 92 | _SetTraceFile(quiet) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 93 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 94 | if first_trace: |
| 95 | _ClearOldTraces() |
| 96 | self._trace_msg = f"{_NEW_COMMAND_SEP} {self._trace_msg}" |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 97 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 98 | def __enter__(self): |
| 99 | if not IsTrace(): |
| 100 | return self |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 101 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 102 | print_msg = ( |
| 103 | f"PID: {os.getpid()} START: {self._time()} :{self._trace_msg}\n" |
| 104 | ) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 105 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 106 | with open(_TRACE_FILE, "a") as f: |
| 107 | print(print_msg, file=f) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 108 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 109 | if _TRACE_TO_STDERR: |
| 110 | print(print_msg, file=sys.stderr) |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 111 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 112 | return self |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 113 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 114 | def __exit__(self, *exc): |
| 115 | if not IsTrace(): |
| 116 | return False |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 117 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 118 | print_msg = ( |
| 119 | f"PID: {os.getpid()} END: {self._time()} :{self._trace_msg}\n" |
| 120 | ) |
LaMont Jones | afd7671 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 121 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 122 | with open(_TRACE_FILE, "a") as f: |
| 123 | print(print_msg, file=f) |
LaMont Jones | afd7671 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 124 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 125 | if _TRACE_TO_STDERR: |
| 126 | print(print_msg, file=sys.stderr) |
LaMont Jones | afd7671 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 127 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 128 | return False |
LaMont Jones | afd7671 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 129 | |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 130 | |
LaMont Jones | ed25be5 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 131 | def _GetTraceFile(quiet): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 132 | """Get the trace file or create one.""" |
| 133 | # TODO: refactor to pass repodir to Trace. |
| 134 | repo_dir = os.path.dirname(os.path.dirname(__file__)) |
| 135 | trace_file = os.path.join(repo_dir, _TRACE_FILE_NAME) |
| 136 | if not quiet: |
| 137 | print(f"Trace outputs in {trace_file}", file=sys.stderr) |
| 138 | return trace_file |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 139 | |
LaMont Jones | afd7671 | 2022-11-10 02:31:19 +0000 | [diff] [blame] | 140 | |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 141 | def _ClearOldTraces(): |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 142 | """Clear the oldest commands if trace file is too big.""" |
| 143 | try: |
| 144 | with open(_TRACE_FILE, "r", errors="ignore") as f: |
| 145 | if os.path.getsize(f.name) / (1024 * 1024) <= _MAX_SIZE: |
| 146 | return |
| 147 | trace_lines = f.readlines() |
| 148 | except FileNotFoundError: |
Joanna Wang | 0324e43 | 2022-12-09 17:49:07 -0500 | [diff] [blame] | 149 | return |
Joanna Wang | a6c52f5 | 2022-11-03 16:51:19 -0400 | [diff] [blame] | 150 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 151 | while sum(len(x) for x in trace_lines) / (1024 * 1024) > _MAX_SIZE: |
| 152 | for i, line in enumerate(trace_lines): |
| 153 | if "END:" in line and _NEW_COMMAND_SEP in line: |
| 154 | trace_lines = trace_lines[i + 1 :] |
| 155 | break |
| 156 | else: |
| 157 | # The last chunk is bigger than _MAX_SIZE, so just throw everything |
| 158 | # away. |
| 159 | trace_lines = [] |
Joanna Wang | 0324e43 | 2022-12-09 17:49:07 -0500 | [diff] [blame] | 160 | |
Gavin Mak | ea2e330 | 2023-03-11 06:46:20 +0000 | [diff] [blame^] | 161 | while trace_lines and trace_lines[-1] == "\n": |
| 162 | trace_lines = trace_lines[:-1] |
| 163 | # Write to a temporary file with a unique name in the same filesystem |
| 164 | # before replacing the original trace file. |
| 165 | temp_dir, temp_prefix = os.path.split(_TRACE_FILE) |
| 166 | with tempfile.NamedTemporaryFile( |
| 167 | "w", dir=temp_dir, prefix=temp_prefix, delete=False |
| 168 | ) as f: |
| 169 | f.writelines(trace_lines) |
| 170 | platform_utils.rename(f.name, _TRACE_FILE) |