mbgtools-lx  4.2.8
mbgxhrtime.c
Go to the documentation of this file.
1 
2 /**************************************************************************
3  *
4  * $Id: mbgxhrtime.c 1.7 2018/11/15 12:12:35 martin TRASH $
5  *
6  * Description:
7  * Main file for mbgxhrtime program which demonstrates how to retrieve
8  * fast and accurate timestamps.
9  *
10  * This program starts a polling thread which reads a high resolution
11  * time stamp and associated CPU cycles counter value once per second
12  * and saves that data pair.
13  *
14  * Current time stamps are then computed by taking the current CPU
15  * cycles value and extrapolating the time from the last data pair.
16  *
17  * This is very much faster than accessing the PCI card for every
18  * single time stamp.
19  *
20  * Notes:
21  *
22  * 1.) This approach works / makes sense only with cards which support
23  * high resolution time stamps (HR time). If a card doesn't support
24  * that then this program prints a warning.
25  *
26  * 2.) Under Linux extrapolation is done using the time stamp counter
27  * (TSC) registers provided by Pentium CPUs and newer/compatible
28  * types as the cycles counter. On SMP / multicore CPUs those
29  * counters may not be synchronized, so this works only correctly
30  * if all cycles counter values are taken from the same CPU.
31  * To achieve this the process CPU affinity is by default set to
32  * the first CPU at program start, which means all threads of this
33  * process are executed only on that CPU.
34  *
35  * 3.) Under Linux there's no easy way to find the accurate clock
36  * frequency of the cycles counter, so the polling thread computes
37  * the frequency from the time differences of 2 subsequent polls
38  * of the PCI card. If the time extrapolation function is called
39  * before the cycles clock frequency has been determined the
40  * returned time stamp is always 0.
41  *
42  * -----------------------------------------------------------------------
43  * $Log: mbgxhrtime.c $
44  * Revision 1.7 2018/11/15 12:12:35 martin
45  * Individual MBG_MICRO_VERSION codes are now obsolete.
46  * Revision 1.6 2017/07/05 18:38:18 martin
47  * New way to maintain version information.
48  * Support build under Windows.
49  * Use more functions from common library modules.
50  * Use codes and inline functions from mbgerror.h.
51  * Proper return codes and exit codes.
52  * Revision 1.5 2009/09/29 14:25:07 martin
53  * Display measured and default PC cycles frequency.
54  * Updated version number to 3.4.0.
55  * Revision 1.4 2009/07/24 09:50:09 martin
56  * Updated version number to 3.3.0.
57  * Revision 1.3 2009/06/19 12:38:52 martin
58  * Updated version number to 3.2.0.
59  * Revision 1.2 2009/03/19 17:04:26 martin
60  * Updated version number to 3.1.0.
61  * Updated copyright year to include 2009.
62  * Revision 1.1 2008/12/22 11:05:24 martin
63  * Initial revision.
64  *
65  **************************************************************************/
66 
67 // include Meinberg headers
68 #include <mbgdevio.h>
69 #include <toolutil.h> // common utility functions
70 
71 // include system headers
72 #include <time.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 
76 #if !defined( MBG_TGT_WIN32 )
77  #include <unistd.h>
78  #include <pthread.h>
79  #include <sched.h>
80 #endif
81 
82 #if !defined( MBGDEVIO_USE_THREAD_API )
83  #error Symbol MBGDEVIO_USE_THREAD_API needs to be defined, see the Makefile.
84 #endif
85 
86 #if !defined( USE_PROCESS_AFFINITY )
87  #define USE_PROCESS_AFFINITY 1
88 #endif
89 
90 
91 #define MBG_FIRST_COPYRIGHT_YEAR 2008
92 #define MBG_LAST_COPYRIGHT_YEAR 0 // use default
93 
94 static const char *pname = "mbgxhrtime";
95 
96 
97 static int loops;
98 
99 
100 
101 #if USE_PROCESS_AFFINITY
102 
103 static /*HDR*/
104 void print_cpu_set( const char *info, MBG_CPU_SET *p_cpu_set )
105 {
106  int min_cpu = MBG_CPU_SET_SIZE + 1;
107  int max_cpu = 0;
108  int i;
109 
110  for ( i = 0; i < MBG_CPU_SET_SIZE; i++ )
111  {
112  if ( !_mbg_cpu_isset( i, p_cpu_set ) )
113  continue;
114 
115  if ( i < min_cpu )
116  min_cpu = i;
117 
118  if ( i > max_cpu )
119  max_cpu = i;
120  }
121 
122  printf( "%s: CPU%i", info, min_cpu );
123 
124  if ( max_cpu == min_cpu )
125  printf( " only" );
126  else
127  printf( "...CPU%i", max_cpu );
128 
129  printf( "\n" );
130 
131 } // print_cpu_set
132 
133 
134 
135 /*HDR*/
137 {
138  MBG_CPU_SET cpu_set;
139 
140  int rc = mbg_get_process_affinity( pid, &cpu_set );
141 
142  if ( rc < 0 )
143  {
144  perror( "Failed to get process affinity mask" );
145  exit( 1 );
146  }
147 
148  print_cpu_set( "Initial process affinity mask", &cpu_set );
149 
150  _mbg_cpu_clear( &cpu_set );
151  _mbg_cpu_set( cpu_num, &cpu_set );
152 
153  rc = mbg_set_process_affinity( pid, &cpu_set );
154 
155  if ( rc < 0 )
156  {
157  perror( "Failed to set process affinity mask" );
158  exit( 1 );
159  }
160 
161  printf( "Process affinity mask set for CPU%i only\n", cpu_num );
162 
163 } // check_set_process_affinity_mask
164 
165 #endif
166 
167 
168 
169 static /*HDR*/
170 int do_mbgxhrtime( MBG_DEV_HANDLE dh, const PCPS_DEV *p_dev )
171 {
172  MBG_PC_CYCLES_FREQUENCY freq_hz = 0;
173  MBG_PC_CYCLES_FREQUENCY default_freq_hz = 0;
174  int this_loops = loops;
175  MBG_POLL_THREAD_INFO poll_thread_info = { { { { 0 } } } };
176  int rc;
177 
178  if ( !_pcps_has_hr_time( p_dev ) )
179  {
180  printf( "High resolution time not supported by this device.\n" );
181  return 0;
182  }
183 
184  mbg_get_default_cycles_frequency_from_dev( dh, &default_freq_hz );
185 
186  rc = mbg_xhrt_poll_thread_create( &poll_thread_info, dh, 0, 0 );
187 
188  if ( rc != MBG_SUCCESS )
189  return rc;
190 
191 
192  for (;;)
193  {
194  static int has_printed_msg = 0;
195 
196  PCPS_HR_TIME hrt;
197  char ws[80];
198  MBG_PC_CYCLES cyc_1;
199  MBG_PC_CYCLES cyc_2;
200  double latency;
201 
202  rc = mbg_get_xhrt_cycles_frequency( &poll_thread_info.xhrt_info, &freq_hz );
203 
204  if ( rc != MBG_SUCCESS )
205  goto fail;
206 
207  if ( freq_hz == 0 )
208  {
209  if ( !has_printed_msg )
210  {
211  printf( "Waiting until PC cycles frequency has been computed ... " );
212  has_printed_msg = 1;
213  }
214 
215  usleep( 50000 );
216  continue;
217  }
218 
219  if ( has_printed_msg )
220  {
221  puts( "" );
222 
223  printf( "PC cycles freq: %.6f MHz", ( (double) (int64_t) freq_hz ) / 1E6 );
224 
225  if ( default_freq_hz )
226  printf( ", default: %.6f MHz", ( (double) (int64_t) default_freq_hz ) / 1E6 );
227 
228  printf( "\n" );
229 
230  has_printed_msg = 0;
231  }
232 
233  // get an extrapolated time stamp bracketed by
234  // mbg_get_pc_cycles() calls to compute the latency
235  mbg_get_pc_cycles( &cyc_1 );
236  rc = mbg_get_xhrt_time_as_pcps_hr_time( &poll_thread_info.xhrt_info, &hrt );
237  mbg_get_pc_cycles( &cyc_2 );
238 
239  if ( rc != MBG_SUCCESS )
240  goto fail;
241 
242  // compute the latency
243  latency = ( (double) cyc_2 - (double) cyc_1 ) / (double) (int64_t) freq_hz * 1E6;
244 
245  // convert to human readable date and time
246  mbg_snprint_hr_time( ws, sizeof( ws ), &hrt, 0 ); // raw timestamp?
247  printf( "t: %s (%.3f us)\n", ws, latency );
248 
249  if ( this_loops > 0 )
250  this_loops--;
251 
252  if ( this_loops == 0 )
253  break;
254 
255  // if this_loops is < 0 then loop forever
256  }
257 
258  goto done;
259 
260 fail:
261  fprintf( stderr, "** Aborting: xhrt function returned %i\n", rc );
262 
263 done:
264  mbg_xhrt_poll_thread_stop( &poll_thread_info );
265 
266  return rc;
267 
268 } // do_mbgxhrtime
269 
271 
272 
273 
274 static /*HDR*/
275 void usage( void )
276 {
278  "This example program reads fast extrapolated high resolution time stamps.\n"
279  "\n"
280  "The program starts an extra polling thread which reads a high resolution\n"
281  "time stamp plus associated PC cycles counter at regular intervals.\n"
282  "The returned time stamps are extrapolated using the current PC cycles\n"
283  "count value plus the last time stamp/cycles pair read by the polling thread.\n"
284  "\n"
285  "This is very much faster than accessing the card every time a time stamp\n"
286  "needs to be retrieved.\n"
287  "This works only for devices which support high resolution time (HR time)."
288  );
290  mbg_print_opt_info( "-c", "run continuously" );
291  mbg_print_opt_info( "-n num", "run num loops" );
293  puts( "" );
294 
295 } // usage
296 
297 
298 
299 int main( int argc, char *argv[] )
300 {
301  int rc;
302  int c;
303 
305 
306  // check command line parameters
307  while ( ( c = getopt( argc, argv, "cn:h?" ) ) != -1 )
308  {
309  switch ( c )
310  {
311  case 'c':
312  loops = -1;
313  break;
314 
315  case 'n':
316  loops = atoi( optarg );
317  break;
318 
319  case 'h':
320  case '?':
321  default:
322  must_print_usage = 1;
323  }
324  }
325 
326  if ( must_print_usage )
327  {
328  usage();
329  return MBG_EXIT_CODE_USAGE;
330  }
331 
332  #if USE_PROCESS_AFFINITY
334  puts( "" );
335  #endif
336 
337  // Handle each of the specified devices.
338  rc = mbg_handle_devices( argc, argv, optind, do_mbgxhrtime, 0 );
339 
341 }
static void usage(void)
Definition: mbgxhrtime.c:275
int mbg_handle_devices(int argc, char *argv[], int optind, MBG_DEV_HANDLER_FNC *fnc, int chk_dev_flags)
Main action handler that can be called by utility programs.
Definition: toolutil.c:655
int must_print_usage
#define _pcps_has_hr_time(_d)
Definition: pcpsdev.h:1149
void check_set_process_affinity_mask(MBG_PROCESS_ID pid, int cpu_num)
Definition: mbgxhrtime.c:136
Requested action completed successfully.
Definition: mbgerror.h:631
#define _mbg_cpu_clear(_ps)
Definition: mbgdevio.h:476
int mbg_snprint_hr_time(char *s, size_t max_len, const PCPS_HR_TIME *p, int show_raw)
Print date and time from a PCPS_HR_TIME structure to a string.
Definition: toolutil.c:851
A structure used to control a poll thread function.
Definition: mbgdevio.h:583
static __mbg_inline void mbg_get_pc_cycles(MBG_PC_CYCLES *p)
Definition: mbgpccyc.h:178
static MBG_DEV_HANDLER_FNC do_mbgxhrtime
Definition: mbgxhrtime.c:270
int main(int argc, char *argv[])
Definition: mbgxhrtime.c:299
Unable to handle requested action, usage printed.
Definition: mbgerror.h:632
#define mbg_rc_is_success(_rc)
Definition: mbgerror.h:618
void mbg_print_device_options(void)
Print common info on how to specify devices on the command line.
Definition: toolutil.c:270
void mbg_print_usage_intro(const char *pname, const char *info)
Print usage intro to console.
Definition: toolutil.c:217
int mbg_get_xhrt_cycles_frequency(MBG_XHRT_INFO *p, MBG_PC_CYCLES_FREQUENCY *p_freq_hz)
Retrieve the frequency of the system&#39;s cycles counter.
static int loops
Definition: mbgxhrtime.c:97
_MBG_API_ATTR int _MBG_API mbg_set_process_affinity(MBG_PROCESS_ID pid, MBG_CPU_SET *p)
Set the CPU affinity of a process.
Definition: mbgdevio.c:8513
MBG_XHRT_INFO xhrt_info
Definition: mbgdevio.h:585
_MBG_API_ATTR int _MBG_API mbg_get_default_cycles_frequency_from_dev(MBG_DEV_HANDLE dh, MBG_PC_CYCLES_FREQUENCY *p)
Retrieve the system&#39;s default cycles counter frequency from the kernel driver.
Definition: mbgdevio.c:9161
_MBG_API_ATTR int _MBG_API mbg_get_process_affinity(MBG_PROCESS_ID pid, MBG_CPU_SET *p)
Read the CPU affinity of a process.
Definition: mbgdevio.c:8470
#define MBG_CPU_SET_SIZE
Definition: mbgdevio.h:472
#define MBG_FIRST_COPYRIGHT_YEAR
Definition: mbgxhrtime.c:91
#define MBG_CPU_SET
Definition: mbgdevio.h:468
__int64 int64_t
Definition: words.h:249
static const char * pname
Definition: mbgxhrtime.c:94
int MBG_DEV_HANDLER_FNC(MBG_DEV_HANDLE, const PCPS_DEV *)
The type of functions to called to handle a device in a specific way.
Definition: toolutil.h:144
#define MBG_SUCCESS
Error codes used with Meinberg devices and drivers.
Definition: mbgerror.h:259
#define _mbg_cpu_set(_i, _ps)
Definition: mbgdevio.h:480
#define MBG_LAST_COPYRIGHT_YEAR
Definition: mbgxhrtime.c:92
Action failed for specified device.
Definition: mbgerror.h:634
uint64_t MBG_PC_CYCLES_FREQUENCY
Definition: mbgpccyc.h:98
#define _mbg_cpu_isset(_i, _ps)
Definition: mbgdevio.h:484
High resolution time including status and local time offset.
Definition: pcpsdefs.h:1085
int mbg_get_xhrt_time_as_pcps_hr_time(MBG_XHRT_INFO *p, PCPS_HR_TIME *p_hrt)
Retrieve an extrapolated time stamp in PCPS_HR_TIME format.
void mbg_print_help_options(void)
Print info on common program help arguments.
Definition: toolutil.c:257
#define _mbg_get_current_process()
Definition: mbgdevio.h:416
void mbg_print_opt_info(const char *opt_name, const char *opt_info)
Print info on a single program option / argument.
Definition: toolutil.c:236
#define MBG_PROCESS_ID
Definition: mbgdevio.h:415
static void print_cpu_set(const char *info, MBG_CPU_SET *p_cpu_set)
Definition: mbgxhrtime.c:104
int mbg_xhrt_poll_thread_stop(MBG_POLL_THREAD_INFO *p_pti)
Stop a polling thread started by mbg_xhrt_poll_thread_create.
void mbg_print_program_info(const char *pname, int first_year, int last_year)
Print program info to console.
Definition: toolutil.c:193
int mbg_xhrt_poll_thread_create(MBG_POLL_THREAD_INFO *p_pti, MBG_DEV_HANDLE dh, MBG_PC_CYCLES_FREQUENCY freq_hz, int sleep_ms)
Set up a MBG_POLL_THREAD_INFO structure and start a new thread.
int64_t MBG_PC_CYCLES
Generic types to hold PC cycle counter values.
Definition: mbgpccyc.h:97
Device info structure.
Definition: pcpsdev.h:1043