blob: 9016cb0cc77c06e18672382f038441909d9b11d3 [file] [log] [blame]
Shawn O. Pearcead3193a2009-04-18 09:54:51 -07001# 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 Frysinger8a11f6f2019-08-27 00:26:15 -040015"""Logic for tracing repo interactions.
16
17Activated via `repo --trace ...` or `REPO_TRACE=1 repo ...`.
Joanna Wanga6c52f52022-11-03 16:51:19 -040018
19Temporary: Tracing is always on. Set `REPO_TRACE=0` to turn off.
20To also include trace outputs in stderr do `repo --trace_to_stderr ...`
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040021"""
22
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070023import sys
24import os
Joanna Wanga6c52f52022-11-03 16:51:19 -040025import time
26from contextlib import ContextDecorator
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040027
Joanna Wang24c63142022-11-08 18:56:52 -050028import platform_utils
29
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040030# Env var to implicitly turn on tracing.
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070031REPO_TRACE = 'REPO_TRACE'
32
Joanna Wanga6c52f52022-11-03 16:51:19 -040033# Temporarily set tracing to always on unless user expicitly sets to 0.
34_TRACE = os.environ.get(REPO_TRACE) != '0'
Joanna Wanga6c52f52022-11-03 16:51:19 -040035_TRACE_TO_STDERR = False
Joanna Wanga6c52f52022-11-03 16:51:19 -040036_TRACE_FILE = None
Joanna Wanga6c52f52022-11-03 16:51:19 -040037_TRACE_FILE_NAME = 'TRACE_FILE'
Joanna Wang24c63142022-11-08 18:56:52 -050038_MAX_SIZE = 70 # in mb
Joanna Wanga6c52f52022-11-03 16:51:19 -040039_NEW_COMMAND_SEP = '+++++++++++++++NEW COMMAND+++++++++++++++++++'
40
41
LaMont Jones47020ba2022-11-10 00:11:51 +000042def IsTraceToStderr():
Joanna Wanga6c52f52022-11-03 16:51:19 -040043 return _TRACE_TO_STDERR
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070044
David Pursehouse819827a2020-02-12 15:20:19 +090045
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070046def IsTrace():
47 return _TRACE
48
David Pursehouse819827a2020-02-12 15:20:19 +090049
Joanna Wanga6c52f52022-11-03 16:51:19 -040050def SetTraceToStderr():
51 global _TRACE_TO_STDERR
52 _TRACE_TO_STDERR = True
53
54
Shawn O. Pearcead3193a2009-04-18 09:54:51 -070055def SetTrace():
56 global _TRACE
57 _TRACE = True
58
David Pursehouse819827a2020-02-12 15:20:19 +090059
Joanna Wanga6c52f52022-11-03 16:51:19 -040060def _SetTraceFile():
61 global _TRACE_FILE
62 _TRACE_FILE = _GetTraceFile()
63
64
65class Trace(ContextDecorator):
66
67 def _time(self):
68 """Generate nanoseconds of time in a py3.6 safe way"""
69 return int(time.time()*1e+9)
70
71 def __init__(self, fmt, *args, first_trace=False):
72 if not IsTrace():
73 return
74 self._trace_msg = fmt % args
75
76 if not _TRACE_FILE:
77 _SetTraceFile()
78
79 if first_trace:
80 _ClearOldTraces()
81 self._trace_msg = '%s %s' % (_NEW_COMMAND_SEP, self._trace_msg)
82
83
84 def __enter__(self):
85 if not IsTrace():
86 return self
87
LaMont Jones47020ba2022-11-10 00:11:51 +000088 print_msg = f'PID: {os.getpid()} START: {self._time()} :' + self._trace_msg + '\n'
Joanna Wanga6c52f52022-11-03 16:51:19 -040089
90 with open(_TRACE_FILE, 'a') as f:
91 print(print_msg, file=f)
92
93 if _TRACE_TO_STDERR:
94 print(print_msg, file=sys.stderr)
95
96 return self
97
98 def __exit__(self, *exc):
99 if not IsTrace():
100 return False
101
LaMont Jones47020ba2022-11-10 00:11:51 +0000102 print_msg = f'PID: {os.getpid()} END: {self._time()} :' + self._trace_msg + '\n'
Joanna Wanga6c52f52022-11-03 16:51:19 -0400103
104 with open(_TRACE_FILE, 'a') as f:
105 print(print_msg, file=f)
106
107 if _TRACE_TO_STDERR:
108 print(print_msg, file=sys.stderr)
109
110 return False
111
112
113def _GetTraceFile():
114 """Get the trace file or create one."""
115 # TODO: refactor to pass repodir to Trace.
116 repo_dir = os.path.dirname(os.path.dirname(__file__))
117 trace_file = os.path.join(repo_dir, _TRACE_FILE_NAME)
LaMont Jonesa3ff64c2022-11-07 23:19:14 +0000118 print('Trace outputs in %s' % trace_file, file=sys.stderr)
Joanna Wanga6c52f52022-11-03 16:51:19 -0400119 return trace_file
120
121def _ClearOldTraces():
Joanna Wang24c63142022-11-08 18:56:52 -0500122 """Clear the oldest commands if trace file is too big.
Joanna Wanga6c52f52022-11-03 16:51:19 -0400123
124 Note: If the trace file contains output from two `repo`
125 commands that were running at the same time, this
126 will not work precisely.
127 """
128 if os.path.isfile(_TRACE_FILE):
129 while os.path.getsize(_TRACE_FILE)/(1024*1024) > _MAX_SIZE:
Joanna Wang24c63142022-11-08 18:56:52 -0500130 temp_file = _TRACE_FILE + '.tmp'
Joanna Wanga6c52f52022-11-03 16:51:19 -0400131 with open(_TRACE_FILE, 'r', errors='ignore') as fin:
Joanna Wang24c63142022-11-08 18:56:52 -0500132 with open(temp_file, 'w') as tf:
133 trace_lines = fin.readlines()
134 for i , l in enumerate(trace_lines):
135 if 'END:' in l and _NEW_COMMAND_SEP in l:
136 tf.writelines(trace_lines[i+1:])
137 break
138 platform_utils.rename(temp_file, _TRACE_FILE)