python plugin monotonic fix (#4156)
* python plugin monotonic fix * python monotonic lib: add SPD header
Ilya Mashchenko committed
Sep 15, 2018 at 04:48 UTC
c1be3331aa7acc209d5c3232ddb13c10900d50c0
5 files changed
+181
-39
REDISTRIBUTED.md
+5
@@ -203,3 +203,8 @@ connectivity is not available.
203
Copyright (C) 2015 Barnaby Gale
204
[MIT License](https://raw.githubusercontent.com/barneygale/MCRcon/master/COPYING.txt)
205
206
+- [monotonic](https://github.com/atdt/monotonic)
207
+
208
+ Copyright 2014, 2015, 2016 Ori Livneh <ori@wikimedia.org>
209
+ [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0)
210
+
python.d/Makefile.am
+1
-1
@@ -86,7 +86,6 @@ dist_bases_DATA = \
86
python_modules/bases/collection.py \
87
python_modules/bases/loaders.py \
88
python_modules/bases/loggers.py \
89
- python_modules/bases/monotonic.py \
89
$(NULL)
90
91
bases_framework_servicesdir=$(basesdir)/FrameworkServices
@@ -107,6 +106,7 @@ dist_third_party_DATA = \
106
python_modules/third_party/lm_sensors.py \
107
python_modules/third_party/mcrcon.py \
108
python_modules/third_party/boinc_client.py \
109
+ python_modules/third_party/monotonic.py \
110
$(NULL)
111
112
pythonyaml2dir=$(pythonmodulesdir)/pyyaml2
python.d/python_modules/bases/FrameworkServices/SimpleService.py
+4
-9
@@ -7,12 +7,7 @@
7
from threading import Thread
8
from time import sleep
9
10
-try:
11
- from time import monotonic as time
12
-except ImportError:
13
- from bases.monotonic import AVAILABLE, time
14
- if not AVAILABLE:
15
- from time import time
10
+from third_party.monotonic import monotonic
11
12
from bases.charts import Charts, ChartError, create_runtime_chart
13
from bases.collection import OldVersionCompatibility, safe_print
@@ -172,7 +167,7 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
167
'retries: {retries}'.format(freq=job.FREQ, retries=job.RETRIES_MAX - job.RETRIES))
168
169
while True:
175
- job.START_RUN = time()
170
+ job.START_RUN = monotonic()
171
172
job.NEXT_RUN = job.START_RUN - (job.START_RUN % job.FREQ) + job.FREQ + job.PENALTY
173
@@ -193,7 +188,7 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
188
if not self.manage_retries():
189
return
190
else:
196
- job.ELAPSED = int((time() - job.START_RUN) * 1e3)
191
+ job.ELAPSED = int((monotonic() - job.START_RUN) * 1e3)
192
job.PREV_UPDATE = job.START_RUN
193
job.RETRIES, job.PENALTY = 0, 0
194
safe_print(RUNTIME_CHART_UPDATE.format(job_name=self.name,
@@ -257,7 +252,7 @@ class SimpleService(Thread, PythonDLimitedLogger, OldVersionCompatibility, objec
252
self.debug('sleeping for {sleep_time} to reach frequency of {freq} sec'.format(sleep_time=sleep_time,
253
freq=job.FREQ + job.PENALTY))
254
sleep(sleep_time)
260
- job.START_RUN = time()
255
+ job.START_RUN = monotonic()
256
257
def get_data(self):
258
return self._get_data()
python.d/python_modules/bases/monotonic.py
deleted
-29
@@ -1,29 +0,0 @@
1
-# thx to https://stackoverflow.com/a/1205762
2
-import ctypes
3
-# import os
4
-
5
-CLOCK_MONOTONIC = 1 # see <linux/time.h>
6
-
7
-
8
-class _Timespec(ctypes.Structure):
9
- _fields_ = [
10
- ('tv_sec', ctypes.c_long),
11
- ('tv_nsec', ctypes.c_long)
12
- ]
13
-
14
-
15
-_librt = ctypes.CDLL('librt.so.1', use_errno=True)
16
-_clock_gettime = _librt.clock_gettime
17
-_clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(_Timespec)]
18
-
19
-
20
-def time():
21
- t = _Timespec()
22
- if _clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(t)) != 0:
23
- # errno_ = ctypes.get_errno()
24
- # raise OSError(errno_, os.strerror(errno_))
25
- return None
26
- return t.tv_sec + t.tv_nsec * 1e-9
27
-
28
-
29
-AVAILABLE = bool(time())
python.d/python_modules/third_party/monotonic.py
new
+171
@@ -0,0 +1,171 @@
1
+# -*- coding: utf-8 -*-
2
+#
3
+# SPDX-License-Identifier: Apache-2.0
4
+"""
5
+ monotonic
6
+ ~~~~~~~~~
7
+
8
+ This module provides a ``monotonic()`` function which returns the
9
+ value (in fractional seconds) of a clock which never goes backwards.
10
+
11
+ On Python 3.3 or newer, ``monotonic`` will be an alias of
12
+ ``time.monotonic`` from the standard library. On older versions,
13
+ it will fall back to an equivalent implementation:
14
+
15
+ +-------------+----------------------------------------+
16
+ | Linux, BSD | ``clock_gettime(3)`` |
17
+ +-------------+----------------------------------------+
18
+ | Windows | ``GetTickCount`` or ``GetTickCount64`` |
19
+ +-------------+----------------------------------------+
20
+ | OS X | ``mach_absolute_time`` |
21
+ +-------------+----------------------------------------+
22
+
23
+ If no suitable implementation exists for the current platform,
24
+ attempting to import this module (or to import from it) will
25
+ cause a ``RuntimeError`` exception to be raised.
26
+
27
+
28
+ Copyright 2014, 2015, 2016 Ori Livneh <ori@wikimedia.org>
29
+
30
+ Licensed under the Apache License, Version 2.0 (the "License");
31
+ you may not use this file except in compliance with the License.
32
+ You may obtain a copy of the License at
33
+
34
+ http://www.apache.org/licenses/LICENSE-2.0
35
+
36
+ Unless required by applicable law or agreed to in writing, software
37
+ distributed under the License is distributed on an "AS IS" BASIS,
38
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
39
+ See the License for the specific language governing permissions and
40
+ limitations under the License.
41
+
42
+"""
43
+import time
44
+
45
+
46
+__all__ = ('monotonic',)
47
+
48
+
49
+try:
50
+ monotonic = time.monotonic
51
+except AttributeError:
52
+ import ctypes
53
+ import ctypes.util
54
+ import os
55
+ import sys
56
+ import threading
57
+ try:
58
+ if sys.platform == 'darwin': # OS X, iOS
59
+ # See Technical Q&A QA1398 of the Mac Developer Library:
60
+ # <https://developer.apple.com/library/mac/qa/qa1398/>
61
+ libc = ctypes.CDLL('/usr/lib/libc.dylib', use_errno=True)
62
+
63
+ class mach_timebase_info_data_t(ctypes.Structure):
64
+ """System timebase info. Defined in <mach/mach_time.h>."""
65
+ _fields_ = (('numer', ctypes.c_uint32),
66
+ ('denom', ctypes.c_uint32))
67
+
68
+ mach_absolute_time = libc.mach_absolute_time
69
+ mach_absolute_time.restype = ctypes.c_uint64
70
+
71
+ timebase = mach_timebase_info_data_t()
72
+ libc.mach_timebase_info(ctypes.byref(timebase))
73
+ ticks_per_second = timebase.numer / timebase.denom * 1.0e9
74
+
75
+ def monotonic():
76
+ """Monotonic clock, cannot go backward."""
77
+ return mach_absolute_time() / ticks_per_second
78
+
79
+ elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
80
+ if sys.platform.startswith('cygwin'):
81
+ # Note: cygwin implements clock_gettime (CLOCK_MONOTONIC = 4) since
82
+ # version 1.7.6. Using raw WinAPI for maximum version compatibility.
83
+
84
+ # Ugly hack using the wrong calling convention (in 32-bit mode)
85
+ # because ctypes has no windll under cygwin (and it also seems that
86
+ # the code letting you select stdcall in _ctypes doesn't exist under
87
+ # the preprocessor definitions relevant to cygwin).
88
+ # This is 'safe' because:
89
+ # 1. The ABI of GetTickCount and GetTickCount64 is identical for
90
+ # both calling conventions because they both have no parameters.
91
+ # 2. libffi masks the problem because after making the call it doesn't
92
+ # touch anything through esp and epilogue code restores a correct
93
+ # esp from ebp afterwards.
94
+ try:
95
+ kernel32 = ctypes.cdll.kernel32
96
+ except OSError: # 'No such file or directory'
97
+ kernel32 = ctypes.cdll.LoadLibrary('kernel32.dll')
98
+ else:
99
+ kernel32 = ctypes.windll.kernel32
100
+
101
+ GetTickCount64 = getattr(kernel32, 'GetTickCount64', None)
102
+ if GetTickCount64:
103
+ # Windows Vista / Windows Server 2008 or newer.
104
+ GetTickCount64.restype = ctypes.c_ulonglong
105
+
106
+ def monotonic():
107
+ """Monotonic clock, cannot go backward."""
108
+ return GetTickCount64() / 1000.0
109
+
110
+ else:
111
+ # Before Windows Vista.
112
+ GetTickCount = kernel32.GetTickCount
113
+ GetTickCount.restype = ctypes.c_uint32
114
+
115
+ get_tick_count_lock = threading.Lock()
116
+ get_tick_count_last_sample = 0
117
+ get_tick_count_wraparounds = 0
118
+
119
+ def monotonic():
120
+ """Monotonic clock, cannot go backward."""
121
+ global get_tick_count_last_sample
122
+ global get_tick_count_wraparounds
123
+
124
+ with get_tick_count_lock:
125
+ current_sample = GetTickCount()
126
+ if current_sample < get_tick_count_last_sample:
127
+ get_tick_count_wraparounds += 1
128
+ get_tick_count_last_sample = current_sample
129
+
130
+ final_milliseconds = get_tick_count_wraparounds << 32
131
+ final_milliseconds += get_tick_count_last_sample
132
+ return final_milliseconds / 1000.0
133
+
134
+ else:
135
+ try:
136
+ clock_gettime = ctypes.CDLL(ctypes.util.find_library('c'),
137
+ use_errno=True).clock_gettime
138
+ except Exception:
139
+ clock_gettime = ctypes.CDLL(ctypes.util.find_library('rt'),
140
+ use_errno=True).clock_gettime
141
+
142
+ class timespec(ctypes.Structure):
143
+ """Time specification, as described in clock_gettime(3)."""
144
+ _fields_ = (('tv_sec', ctypes.c_long),
145
+ ('tv_nsec', ctypes.c_long))
146
+
147
+ if sys.platform.startswith('linux'):
148
+ CLOCK_MONOTONIC = 1
149
+ elif sys.platform.startswith('freebsd'):
150
+ CLOCK_MONOTONIC = 4
151
+ elif sys.platform.startswith('sunos5'):
152
+ CLOCK_MONOTONIC = 4
153
+ elif 'bsd' in sys.platform:
154
+ CLOCK_MONOTONIC = 3
155
+ elif sys.platform.startswith('aix'):
156
+ CLOCK_MONOTONIC = ctypes.c_longlong(10)
157
+
158
+ def monotonic():
159
+ """Monotonic clock, cannot go backward."""
160
+ ts = timespec()
161
+ if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(ts)):
162
+ errno = ctypes.get_errno()
163
+ raise OSError(errno, os.strerror(errno))
164
+ return ts.tv_sec + ts.tv_nsec / 1.0e9
165
+
166
+ # Perform a sanity-check.
167
+ if monotonic() - monotonic() > 0:
168
+ raise ValueError('monotonic() is not monotonic!')
169
+
170
+ except Exception as e:
171
+ raise RuntimeError('no suitable implementation for this system: ' + repr(e))