/**
 * \fn void main (void)
 * \brief Création du compteur "nom_compteur" dans /opt/productivity_link
 * \return EXIT_SUCCESS code de statut : ok. 
 * 
 */
int main (void) {
  /** Le type uuid permet de donner un identificateur au compteur */  
  uuid_t uuid; 
  
  unsigned long long nombre=0 ;
  int pld = PL_INVALID_DESCRIPTOR;
  
  /** Le nom du compteur est stocké
   * dans une constante de pointeur de chaine de caractères
   * const char *counter[].
   */
  const char *counter[] = {"grandeur_a_analyser"};
  
  /// Ouverture et test du compteur.
  if ((pld = pl_open("nom_compteur",1,counter,&uuid)) != PL_INVALID_DESCRIPTOR){
    
    /// Incrémentation de "nombre" jusqu'à 21.
    while (nombre<=21){
      pl_write(pld,&nombre,0);
      nombre++;
      sleep(1);
    }

    /// Décrémentation de "nombre" jusqu'à 5.
    while (nombre>5){
      nombre--;
      pl_write(pld,&nombre,0);
      sleep(1);
    }
    
    /// Fermeture du compteur
    pl_close(pld);
  }
  
  return EXIT_SUCCESS;
}
//-----------------------------------------------------------------------------
// Program entry point - main
//-----------------------------------------------------------------------------
int main(int argc, char *argv[]) {

	//-------------------------------------------------------------------------
	// Generic variables.
	//-------------------------------------------------------------------------
	int i = 0;
	int ret = PL_FAILURE;
#ifdef __PL_WINDOWS__
	BOOL b_ret = FALSE;
	size_t st_ret = 0;
	#ifdef __PL_FILESYSTEM_LESS__
		DWORD dw_ret = 0;
	#endif // __PL_FILESYSTEM_LESS__
#endif // __PL_WINDOWS__

	//-------------------------------------------------------------------------
	// PL management variables.
	//-------------------------------------------------------------------------
	int pld = PL_INVALID_DESCRIPTOR;

	//-------------------------------------------------------------------------
	// File operation management variables.
	//-------------------------------------------------------------------------
	char file[PL_MAX_PATH];
#if defined (__PL_WINDOWS__) && !defined (__PL_FILESYSTEM_LESS__)
	OPENFILENAME ofn;
	TCHAR t_file[PL_MAX_PATH];
#endif // __PL_WINDOWS__ && !__PL_FILESYSTEM_LESS__

	//-------------------------------------------------------------------------
	// Counter data management variables.
	//-------------------------------------------------------------------------
#ifdef __PL_WINDOWS__
	ULONGLONG performance = 0;
	ULONGLONG work_units = 0;
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__) 
	unsigned long long performance = 0;
	unsigned long long work_units = 0;
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	//-------------------------------------------------------------------------

#if defined (__PL_WINDOWS__) && !defined (__PL_FILESYSTEM_LESS__)

	//-------------------------------------------------------------------------
	// Open a file selection dialog box.
	//-------------------------------------------------------------------------
	ZeroMemory(
		t_file, 
		sizeof(t_file)
	);
	ZeroMemory(
		&ofn, 
		sizeof(ofn)
	);
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = NULL;
	ofn.lpstrFile = t_file;
	ofn.nMaxFile = sizeof(t_file);
	ofn.lpstrFilter = _T("Productivity Link Configuration File\0*.INI\0");
	ofn.nFilterIndex = 1;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrInitialDir = PL_FOLDER;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	b_ret = GetOpenFileName(&ofn);
	if(!b_ret) {
		return(1); // signal error
	}
	ZeroMemory(
		file, 
		sizeof(file)
	);
	wcstombs_s(
		&st_ret, 
		file, 
		sizeof(file), 
		ofn.lpstrFile, 
		_TRUNCATE
	);
#endif // __PL_WINDOWS__ && !__PL_FILESYSTEM_LESS__

	//-------------------------------------------------------------------------
	// Check input arguments count and retrive file name (no checks on name).
	//-------------------------------------------------------------------------
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__) 
	if(argc != 2) {
		fprintf(
			stderr, 
			"\nUsage: %s <pl_config_file>\n\n", 
			argv[0]
		);
		exit(1); // signal error
	}
	memset(
		file, 
		0, 
		sizeof(file)
	);
	strncpy(
		file, 
		argv[1],
		strlen(argv[1])
	);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
#if defined (__PL_WINDOWS__) && defined (__PL_FILESYSTEM_LESS__)
	if(argc != 2) {
		fprintf(
			stderr, 
			"\nUsage: %s <pl_config_file>\n\n", 
			argv[0]
		);
		exit(1); // signal error
	}
	memset(
		file, 
		0, 
		sizeof(file)
	);
	strncpy(
		file, 
		argv[1],
		strlen(argv[1])
	);
#endif // __PL_WINDOWS__ && __PL_FILESYSTEM_LESS__

	//-------------------------------------------------------------------------
	// Display PL directory.
	//-------------------------------------------------------------------------
	printf("\n*******************************************************\n");
	printf("Using productivity link directory: %s\n", file);
	printf("*******************************************************\n\n");

	//-------------------------------------------------------------------------
	// Attach to PL.
	//-------------------------------------------------------------------------
	pld = pl_attach(file);
	if(pld == PL_INVALID_DESCRIPTOR) {
#ifdef __PL_WINDOWS__
		fprintf(
			stdout,
			"pl_attach() error: [%d]d - [%x]h\n", 
			GetLastError(),
			GetLastError()
		);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || defined (__PL_SOLARIS__) || (__PL_MACOSX__) 
		fprintf(
			stdout,
			"pl_attach() error: [%d]d - [%x]h\n", 
			errno,
			errno
		);
		fflush(stdout);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		assert(0);
	}

	//-------------------------------------------------------------------------
	// Read PL counters ITERATIONS times.
	//-------------------------------------------------------------------------
	for(i = 0; i < ITERATIONS; i++) {
		printf(
			"Iteration %d\t", 
			i
		);
#ifndef __PL_BLOCKING_COUNTER_FILE_LOCK__
		ret = pl_read(
			pld, 
			&performance, 
			PERFORMANCE
		); 
		check(ret);
		ret = pl_read(
			pld, 
			&work_units, 
			WORK_UNITS
		);  
		check(ret);
#else // __PL_BLOCKING_COUNTER_FILE_LOCK__
		ret = pl_read(
			pld, 
			&performance, 
			PERFORMANCE
		); 
		if(ret != PL_SUCCESS) {
#ifdef __PL_WINDOWS__
			fprintf(
				stdout,
				"pl_read() error: [%d]d - [%x]h\n", 
				GetLastError(),
				GetLastError()
			);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || defined (__PL_SOLARIS__) || (__PL_MACOSX__) 
			fprintf(
				stdout,
				"pl_read() error: [%d]d - [%x]h\n", 
				errno,
				errno
			);
			fflush(stdout);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
			assert(0);
		}
		ret = pl_read(
			pld, 
			&work_units, 
			WORK_UNITS
		); 
		assert(ret == PL_SUCCESS);

#endif // __PL_BLOCKING_COUNTER_FILE_LOCK__

	//-------------------------------------------------------------------------
	// Pause and echo counters values.
	//-------------------------------------------------------------------------
#ifdef __PL_WINDOWS__
		Sleep(PAUSE);
		printf(
			"performance: %I64u, work_units: %I64u\n", 
			performance, 
			work_units
		);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__) 
		sleep(PAUSE);
		printf(
			"performance: %lld, work_units: %lld\n", 
			performance, 
			work_units
		);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	} // for i

	//-------------------------------------------------------------------------
	// Clean-up.
	//-------------------------------------------------------------------------
	ret = pl_close(pld);
	if(ret != PL_SUCCESS) {
#ifdef __PL_WINDOWS__
		fprintf(
			stdout,
			"pl_close() error: [%d]d - [%x]h\n", 
			GetLastError(),
			GetLastError()
		);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || defined (__PL_SOLARIS__) || (__PL_MACOSX__) 
		fprintf(
			stdout,
			"pl_close() error: [%d]d - [%x]h\n", 
			errno,
			errno
		);
		fflush(stdout);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		assert(0);
	} else {
		pld = PL_INVALID_DESCRIPTOR;
	}
	return(0);
}
//-----------------------------------------------------------------------------
// program entry point.
//-----------------------------------------------------------------------------
int main(int argc, char *argv[], char *envp[]) {

	//-------------------------------------------------------------------------
	// Generic variables.
	//-------------------------------------------------------------------------
	int ret = -1;
	PENERGY_DATA p = NULL;
	PL_STATUS plret = PL_FAILURE; 
	unsigned long long value = 0;
	char buffer[ENERGY_INPUT_LINE_MAX_SIZE] = { '\0' };
#ifdef __PL_WINDOWS__
	BOOL bret = FALSE;
#endif // __PL_WINDOWS__

	//-------------------------------------------------------------------------
	// ESRV instance handling variables.
	//-------------------------------------------------------------------------
	char esrv_config_file_name_buffer[PL_MAX_PATH] = { '\0' };
	int f_esrv_guid = 0;
	int f_esrv_pid = 0;
	char *env = NULL;

	//-------------------------------------------------------------------------
	// Variables used to build ESRV start command -- if we have to.
	//-------------------------------------------------------------------------
	char esrv_command_buffer[PL_MAX_PATH] = { '\0' };

	//-------------------------------------------------------------------------
	// Variables used to remove ESRV PL after use -- if we started it.
	//-------------------------------------------------------------------------
	char esrv_pl_path_name[PL_MAX_PATH] = { '\0' };
#ifdef __PL_WINDOWS__
	char current_path_name[PL_MAX_PATH] = { '\0' };
	char esrv_pl_config_file_name[PL_MAX_PATH] = { '\0' };
	struct _finddata_t find_files;
	intptr_t file = 0;
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
	DIR *directory = NULL;
	struct dirent *file = NULL;
	char file_name[PL_MAX_PATH] = { '\0' };
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	//-------------------------------------------------------------------------
	// Variables used to read ESRV's sartup output -- used to get the GUID
	//-------------------------------------------------------------------------
	char *pc = NULL;
	char *token = NULL;
	int input_line_count = 0;
	char esrv_guid[ENERGY_GUID_LENGHT_IN_CHARACTERS + 1] = { '\0' };
#ifdef __PL_WINDOWS__
	DWORD esrv_pid = 0;
	HANDLE esrv_handle = NULL;
	DWORD bytes_read = 0;
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
	pid_t esrv_pid = 0;
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	//-------------------------------------------------------------------------
	// Signal handler variables.
	//-------------------------------------------------------------------------
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)  
	struct sigaction sa;
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	//--------------------------------------------------------------------------

	//-------------------------------------------------------------------------
	// Retrive the energy structure address.
	//-------------------------------------------------------------------------
	p = &energy_data;
	assert(p != NULL);

	//-------------------------------------------------------------------------
	// Initialize the energy data structure.
	//-------------------------------------------------------------------------
	memset(p, 0, sizeof(ENERGY_DATA));
	p->argc = argc;
	p->argv = argv;
	p->channel = ENERGY_DEFAULT_CHANNEL;
	p->esrv_pld = PL_INVALID_DESCRIPTOR;
	p->esrv_status = ESRV_STATUS_NOT_RUNNING;

	//-------------------------------------------------------------------------
	// Parse user input.
	//-------------------------------------------------------------------------
	plret = parser(&energy_data);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to make sense of user input.");
	} else {
		if((p->f_help == 1) || (p->f_version == 1)) {
			goto done;
		}
	}

	//-------------------------------------------------------------------------
	// Check for inconsistent user input.
	//-------------------------------------------------------------------------
	if(
		(
			(p->f_guid == 1) && 
			(p->f_guid_shell_variable)
		)
	) {
		_ERROR(
			"Incompatible ESRV instance designation.  Please use --guid or --guid_shell_variable option."
		);
	}
	if(p->f_command == 0) {
		_ERROR(
			"You need to specify a command.  Use energy --help for details."
		);
	}

	//-------------------------------------------------------------------------
	// Select the ESRV instance to use.
	// Note:
	//    If no ESRV id is provided, then an instance is started and ended
	//    by the program.
	// Note:
	//    If GUID is provided, build the pl_config.ini full path name for the
	//    attach.
	// Note:
	//    If CONFIG is provided, then do nothing, we are ready to attach.
	//-------------------------------------------------------------------------
	memset(
		esrv_config_file_name_buffer, 
		0, 
		sizeof(esrv_config_file_name_buffer)
	);
	if(p->f_guid == 1) {
		memcpy(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_ROOT, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_ROOT)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ESRV_APPLICATION_NAME, 
			strlen(ESRV_APPLICATION_NAME)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE)
		);
		strncat(
			esrv_config_file_name_buffer, 
			p->esrv_guid, 
			strlen(p->esrv_guid)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_NAME, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_NAME)
		);
		goto attach_to_esrv;
	}
	if(p->f_guid_shell_variable == 1) {
		memcpy(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_ROOT, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_ROOT)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ESRV_APPLICATION_NAME, 
			strlen(ESRV_APPLICATION_NAME)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE)
		);
		strncat(
			esrv_config_file_name_buffer, 
			p->esrv_guid_shell_variable_value, 
			strlen(p->esrv_guid_shell_variable_value)
		);
		strncat(
			esrv_config_file_name_buffer, 
			ENERGY_ESRV_PL_CONFIG_FILE_NAME, 
			strlen(ENERGY_ESRV_PL_CONFIG_FILE_NAME)
		);
		goto attach_to_esrv;
	}

	//-------------------------------------------------------------------------
	// Because it is easier to type energy -- command than energy --guid xxx --
	// command, we check for the existence of a ENERGY_ESRV_DEFAULT_GUID_SHELL_-
	// -VARIABLE shell variable.  If it exist and has a valid GUID, then it is 
	// used.  If no such variable exist, then energy continues by starting its
	// own instance of ESRV.
	//-------------------------------------------------------------------------
	env = getenv(ENERGY_ESRV_DEFAULT_GUID_SHELL_VARIABLE); 
	if(env != NULL) {
		ret = plh_filter_uuid_string(env);
		if(ret != PL_FAILURE) {
			memcpy(
				esrv_config_file_name_buffer, 
				ENERGY_ESRV_PL_CONFIG_FILE_ROOT, 
				strlen(ENERGY_ESRV_PL_CONFIG_FILE_ROOT)
			);
			strncat(
				esrv_config_file_name_buffer, 
				ESRV_APPLICATION_NAME, 
				strlen(ESRV_APPLICATION_NAME)
			);
			strncat(
				esrv_config_file_name_buffer, 
				ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE, 
				strlen(ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE)
			);
			strncat(
				esrv_config_file_name_buffer, 
				env, 
				strlen(env)
			);
			strncat(
				esrv_config_file_name_buffer, 
				ENERGY_ESRV_PL_CONFIG_FILE_NAME, 
				strlen(ENERGY_ESRV_PL_CONFIG_FILE_NAME)
			);
			goto attach_to_esrv;
		}
	}

	//-------------------------------------------------------------------------
	// Build ESRV binary name and command line
	// Note:
	//   cli_buffer holds the ESRV binary name and the command line options.
	//   if the command and the arguments are provided separately to
	//   CreateProcess then the argv count is erroneous in the started 
	//   process and ESRV fails the cli parsing.
	//-------------------------------------------------------------------------
	memset(
		esrv_command_buffer, 
		0, 
		sizeof(esrv_command_buffer)
	);
	strncpy(
		esrv_command_buffer, 
		ENERGY_ESRV_BINARY_NAME, 
		strlen(ENERGY_ESRV_BINARY_NAME)
	);
	if(p->f_esrv_options == 1) {
		strncat(
			esrv_command_buffer, 
			p->esrv_options, 
			strlen(p->esrv_options)
		);
	} else {
		strncat(
			esrv_command_buffer, 
			ENERGY_ESRV_DEFAULT_OPTIONS, 
			strlen(ENERGY_ESRV_DEFAULT_OPTIONS)
		);
	}
	strncat(
		esrv_command_buffer, 
		ENERGY_ESRV_SHELL_OPTION, 
		strlen(ENERGY_ESRV_SHELL_OPTION)
	);

	//-------------------------------------------------------------------------
	// Start an ESRV instance in a child process.
	//-------------------------------------------------------------------------
#ifdef __PL_WINDOWS__
	p->fp_esrv = _popen(
		esrv_command_buffer, 
		"rt"
	);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
	p->fp_esrv = popen(
		esrv_command_buffer, 
		"r"
	);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
	if(p->fp_esrv == NULL) {
		_ERROR("Unable to start ESRV.");
	}

	//-------------------------------------------------------------------------
	// Retrieve the ESRV's instance PID and GUID.
	//-------------------------------------------------------------------------
	do {
		pc = fgets(
			buffer, 
			sizeof(buffer), 
			p->fp_esrv
		);
		if(pc != NULL) {
			switch(++input_line_count) {

				case ENERGY_ESRV_GUID_LINE:

					//---------------------------------------------------------
					// extract ESRV's GUID and save it
					//---------------------------------------------------------
					token = strtok(
						buffer, 
						ENERGY_ESRV_GUID_TOKEN_SEPARATORS
					);
					while(token != NULL) {
						if(strncmp(
							token, 
							ENERGY_ESRV_PRE_GUID_TOKEN, 
							strlen(ENERGY_ESRV_PRE_GUID_TOKEN)
						) == 0) { 
							token = strtok(
								NULL, 
								ENERGY_ESRV_GUID_TOKEN_TERMINATOR
							);
							memset(
								esrv_guid, 
								0, 
								sizeof(esrv_guid)
							);
							strncpy(
								esrv_guid, 
								token, 
								strlen(token)
							);
							f_esrv_guid = 1;
							break;
						}
						token = strtok(
							NULL, 
							ENERGY_ESRV_GUID_TOKEN_SEPARATORS
						);
					}
					break;

				case ENERGY_ESRV_PID_LINE:

					//---------------------------------------------------------
					// extract ESRV's PID and save it.
					//---------------------------------------------------------
					token = strtok(
						buffer, 
						ENERGY_ESRV_PID_TOKEN_SEPARATORS
					);
					while(token != NULL) {
						if(strncmp(
							token, 
							ENERGY_ESRV_PRE_PID_TOKEN, 
							strlen(ENERGY_ESRV_PRE_PID_TOKEN)
						) == 0) { 
							token = strtok(
								NULL, 
								ENERGY_ESRV_PID_TOKEN_TERMINATOR
							);
#ifdef __PL_WINDOWS__
							esrv_pid = (DWORD)atoi(token);
#endif // __Pl_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
							esrv_pid = (pid_t)atoi(token);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
							assert(esrv_pid != 0);
							f_esrv_pid = 1;
							goto pid_found;
						}
						token = strtok(
							NULL, 
							ENERGY_ESRV_GUID_TOKEN_SEPARATORS
						);
					}
					break;

				default:
					break;
			}
		} else {

			//-----------------------------------------------------------------
			// Likely the ESRV launch has failed, let's signal this error.
			//-----------------------------------------------------------------
			_ERROR("ESRV likely failed to start.");
		}
	} while(pc != NULL);

	//-------------------------------------------------------------------------
	// Likely the ESRV launch has failed, let's signal this error.
	//-------------------------------------------------------------------------
	_ERROR("ESRV likely failed to start.");

pid_found:

	//-------------------------------------------------------------------------
	// Check and build the pl_config.ini file to attach to.
	//-------------------------------------------------------------------------
	assert((f_esrv_guid == 1) && (f_esrv_pid == 1));
	memset(
		esrv_config_file_name_buffer, 
		0, 
		sizeof(esrv_config_file_name_buffer)
	);
	memcpy(
		esrv_config_file_name_buffer, 
		ENERGY_ESRV_PL_CONFIG_FILE_ROOT, 
		strlen(ENERGY_ESRV_PL_CONFIG_FILE_ROOT)
	);
	strncat(
		esrv_config_file_name_buffer, 
		ESRV_APPLICATION_NAME, 
		strlen(ESRV_APPLICATION_NAME)
	);
	strncat(
		esrv_config_file_name_buffer, 
		ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE, 
		strlen(ENERGY_ESRV_PL_CONFIG_FILE_UNDERSCORE)
	);
	strncat(
		esrv_config_file_name_buffer, 
		esrv_guid, 
		strlen(esrv_guid)
	);
	memset(
		esrv_pl_path_name, 
		0, 
		sizeof(esrv_pl_path_name)
	);
	strncpy(
		esrv_pl_path_name, 
		esrv_config_file_name_buffer, 
		strlen(esrv_config_file_name_buffer)
	);
	strncat(
		esrv_config_file_name_buffer, 
		ENERGY_ESRV_PL_CONFIG_FILE_NAME, 
		strlen(ENERGY_ESRV_PL_CONFIG_FILE_NAME)
	);

attach_to_esrv:

	//-------------------------------------------------------------------------
	// Attach to the identified instance of ESRV and read settings.
	//-------------------------------------------------------------------------
	p->esrv_pld = pl_attach(esrv_config_file_name_buffer);
	if(p->esrv_pld == PL_INVALID_DESCRIPTOR) {
		_ERROR("Unable to attach to the specified ESRV instance.");
	}

	//-------------------------------------------------------------------------
	// read-in esrv's configuration:
	//  - ESRV Status (running or not)
	//  - ESRV Channel count
	//  - ESRV Version (not used)
	//  - ESRV energy in joule counter's precision
	// Note:
	//    since each channel holds esrv's configuration counters, we read the 
	//    first channel to read the channel count.  ESRV has always at least
	//    one channel, so the read is safe
	// Note:
	//    Channel count is zero count, therefore the --.
	//-------------------------------------------------------------------------
	plret = pl_read(
		p->esrv_pld, 
		&p->channels, 
		ESRV_COUNTER_CHANNELS_INDEX
	);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to read the ESRV channels count counter.");
	}
	if(p->channel > p->channels) {
		WARNING(
			"The requested channel does not exist in the specified ESRV instance.  Will use default channel (1)."
		);
		p->channel = 1; 
	}
	p->channel--;

	//-------------------------------------------------------------------------
	// Now that the channels count is known, we can read the requested ESRV channel
	//-------------------------------------------------------------------------
	plret = pl_read(
		p->esrv_pld, 
		&p->status, 
		(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_STATUS_INDEX
	);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to read the ESRV status counter.");
	}
	if(p->status != ESRV_STATUS_RUNNING) {
		_ERROR("The specified ESRV instance doesn't seem to be alive.");
	}
	plret = pl_read(
		p->esrv_pld, 
		&p->version, 
		(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_VERSION_INDEX
	); 
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to read the ESRV version counter.");
	}
	plret = pl_read(
		p->esrv_pld, 
		&value, 
		(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_ENERGY_JOULES_DECIMALS_INDEX
	); 
	if(plret != PL_SUCCESS) {
		_ERROR(
			"Unable to read the ESRV energy in Joule(s) counter's .decimal suffix counter."
		);
	}
	p->energy_data_multiplier = pow(
		10.0, 
		(double)value
	);

	//-------------------------------------------------------------------------
	// Install signal handler.
	//-------------------------------------------------------------------------
#ifdef __PL_WINDOWS__
	bret = SetConsoleCtrlHandler(
		(PHANDLER_ROUTINE)signal_handler, 
		TRUE
	);
	if(bret == 0) {
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)  
	sa.sa_handler = signal_handler;
	sigemptyset(&sa.sa_mask);	
	sa.sa_flags = 0;
	ret = sigaction(
		SIGINT, 
		&sa, 
		NULL
	);
	if(ret == -1) {
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		_ERROR("Unable to install the signal handler."); 
	}

	//-------------------------------------------------------------------------
	// Read start energy.
	//-------------------------------------------------------------------------
	plret = pl_read(
		p->esrv_pld, 
		&p->start_energy_data, 
		(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_ENERGY_JOULES_INDEX
	);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to read start energy value.");
	}

	//-------------------------------------------------------------------------
	// Run command.
	//-------------------------------------------------------------------------
#ifdef __PL_WINDOWS__
	p->fp = _popen(
		p->command, 
		"rt"
	);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)  
	p->fp = popen(
		p->command, 
		"r"
	);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
	if(p->fp == NULL) {
		_ERROR("Unable to execute command.");
	}

	//-------------------------------------------------------------------------
	// Echo command output to stdout.
	// Note:
	//    With some verbose commands, the extra processing on the output may
	//    consume extra energy (buffer[strlen(buffer) - 1] = '\0';).  Remove
	//    this cosmetic processing if this becomes an issue.
	//-------------------------------------------------------------------------
	while(p->f_interrupted == 0) {
		pc = fgets(
			buffer, 
			sizeof(buffer), 
			p->fp
		);
		if(pc != NULL) {
			buffer[strlen(buffer) - 1] = '\0';
			puts(buffer);
		} else {
			break;
		}
	}

	//-------------------------------------------------------------------------
	// Read end energy and close ESRV PL.
	//-------------------------------------------------------------------------
	plret = pl_read(
		p->esrv_pld, 
		&p->end_energy_data, 
		(p->channel * ESRV_BASE_COUNTERS_COUNT) + ESRV_COUNTER_ENERGY_JOULES_INDEX
	);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to read end energy value.");
	}
	plret = pl_close(p->esrv_pld);
	if(plret != PL_SUCCESS) {
		_ERROR("Unable to close ESRV instance's PL.");
	}

	//-------------------------------------------------------------------------
	// End command.
	//-------------------------------------------------------------------------
	if(p->f_interrupted == 0) {
		if(feof(p->fp)) {
#ifdef __PL_WINDOWS__
			_pclose(p->fp);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
			pclose(p->fp);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		} else {
			_ERROR("Unable to completely read command's output.");
		}
	}

	//-------------------------------------------------------------------------
	// Compute energy consumed.
	//-------------------------------------------------------------------------
	p->consumed_energy_in_joules = (double)(
			p->end_energy_data - 
			p->start_energy_data
		) / 
		p->energy_data_multiplier
	;
	p->consumed_energy_in_kwhs = 
		p->consumed_energy_in_joules / 
		ONE_KWH_IN_JOULES
	;

	//-------------------------------------------------------------------------
	// Report consumed energy.
	//-------------------------------------------------------------------------
	fprintf(
		stdout, 
		"\nEnergy: [%g] Joule(s) - [%g] kWh(s).\n", 
		p->consumed_energy_in_joules, 
		p->consumed_energy_in_kwhs
	);

	//-------------------------------------------------------------------------
	// close the ESRV instance's process and remove its PL
	//-------------------------------------------------------------------------
	if(
		(f_esrv_guid == 1) && 
		(f_esrv_pid == 1)
	) {
#ifdef __PL_WINDOWS__
		esrv_handle = OpenProcess(
			PROCESS_TERMINATE, 
			FALSE, 
			esrv_pid
		);
		assert(esrv_handle != NULL);
		bret = TerminateProcess(
			esrv_handle, 
			0
		);
		assert(bret != FALSE);

		//---------------------------------------------------------------------
		// reset last ESRV instance's flags and ids 
		//---------------------------------------------------------------------
		esrv_handle = NULL;
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
		ret = kill(
			esrv_pid, 
			SIGTERM
		); 
		assert(ret != -1);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		f_esrv_guid = 0;
		f_esrv_pid = 0;
		esrv_pid = 0;

		//---------------------------------------------------------------------
		// close last ESRV instance's output stream
		//---------------------------------------------------------------------
#ifdef __PL_WINDOWS__
		_pclose(p->fp_esrv);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
		pclose(p->fp_esrv);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

		//---------------------------------------------------------------------
		// Delete ESRV instance's PL.
		//---------------------------------------------------------------------
#ifdef __PL_WINDOWS__
		pc = _getcwd(
			current_path_name, 
			sizeof(current_path_name)
		); 
		assert(pc != NULL);		
		ret = _chdir(esrv_pl_path_name); 
		assert(ret != -1);
		file = _findfirst(
			"*", 
			&find_files
		);
		do {
			if(
				(
					strcmp(
						find_files.name, 
						"."
					) != 0
				) && 
				(
					strcmp(
						find_files.name, 
						".."
					) != 0
				)
			) {
				ret = -1;
				do { 
					ret = remove(find_files.name); 
				} while(ret == -1);
			}
		} while(
			_findnext(
				file, 
				&find_files
			) == 0);
		ret = _findclose(file); 
		assert(ret != -1);
		ret = _chdir(current_path_name); 
		assert(ret != -1);
		ret = -1;
		do { 
			ret = _rmdir(esrv_pl_path_name); 
		} while(ret == -1);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
		directory = opendir(esrv_pl_path_name);
		assert(directory != NULL);
		file = readdir(directory);
		while(file != NULL) {
			if(
				(
					strcmp(
						file->d_name, 
						"."
					) != 0
				) && 
				(
					strcmp(
						file->d_name, 
						".."
					) != 0
				)
			) {
				memset(
					file_name, 
					0, 
					sizeof(file_name)
				);
				strncat(
					file_name, 
					esrv_pl_path_name, 
					strlen(esrv_pl_path_name)
				);
				strncat(
					file_name, 
					"/", 
					strlen("/")
				);
				strncat(
					file_name, 
					file->d_name, 
					strlen(file->d_name)
				);
				ret = -1;
				do { 
					ret = unlink(file_name); 
				} while(ret != -1);
			}
			file = readdir(directory);
		}
		closedir(directory);
		ret = -1;
		do { 
			ret = rmdir(esrv_pl_path_name); 
		} while(ret != -1);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
	}
done:
	return(PL_SUCCESS);
error:
	return(PL_FAILURE);
}

/*-----------------------------------------------------------------------------
Function: parser
Purpose : parse user input to set energy data structure
In      : pointer to energy data structure
Out     : updated energy data structure
Return  : status

History
-------------------------------------------------------------------------------
Date        : Author                  Modification
-------------------------------------------------------------------------------
01/28/2010    Jamel Tayeb             Creation.
*/
int parser(PENERGY_DATA p) {

	//-------------------------------------------------------------------------
	// Parsing variables.
	//-------------------------------------------------------------------------
	int i = 0;
	int j = 0;
	int value = 0;
	char buffer[PL_MAX_PATH] = { '\0' };
	char *options[OPTION_STRINGS_COUNT] = { OPTION_STRINGS };
#ifdef __PL_WINDOWS__
	size_t st_ret = 0;
#endif // __PL_WINDOWS__
	PL_STATUS ret = PL_FAILURE;

	//-------------------------------------------------------------------------
	// String to upper case variables.
	//-------------------------------------------------------------------------
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__) 
	size_t k = 0;
	size_t l = 0;
	char *pc = NULL;
	char c = '\0';
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

	assert(p != NULL);

	for(i = 1; i < p->argc; i++) {
		memset(buffer, 0, sizeof(buffer));
		strncpy(buffer, p->argv[i], sizeof(buffer));
#ifdef __PL_WINDOWS__
		_strupr(buffer);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
		pc = buffer;
		l = strlen(buffer);
		for(k = 0; k < l; k++) {
			c = *pc;
			*pc++ = (char)toupper(c);
		}
#endif// __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
		for(j = 0; j < OPTION_STRINGS_COUNT; j++) {
			if(strncmp(buffer, options[j], strlen(options[j])) == 0) {
				switch(j) {

					//---------------------------------------------------------
					// [-H] option.
					//---------------------------------------------------------
					case H_ID:
					case HELP_ID:
						fprintf(
							stdout, 
							HELP_STRING, 
							ENERGY_APPLICATION_NAME, 
							ENERGY_APPLICATION_NAME, 
							ENERGY_APPLICATION_NAME,
							ENERGY_ESRV_DEFAULT_GUID_SHELL_VARIABLE,
							ENERGY_APPLICATION_NAME, 
							ENERGY_APPLICATION_NAME, 
							ENERGY_APPLICATION_NAME
						);
						p->f_help = 1;
						goto parser_done;
						break;

					//---------------------------------------------------------
					// [-V] option.
					//---------------------------------------------------------
					case V_ID:
					case VERSION_ID:
						p->f_version = 1;
						fprintf(
							stdout, 
							"%s: version %s.%s.%s\n", 
							ENERGY_APPLICATION_NAME, 
							ENERGY_VERSION_MAJOR, 
							ENERGY_VERSION_MINOR, 
							ENERGY_VERSION_REVISION
						);
						fprintf(
							stdout, 
							"Using PL helper version %s.%s.%s\n", 
							PL_HELPER_VERSION_MAJOR, 
							PL_HELPER_VERSION_MINOR, 
							PL_HELPER_VERSION_REVISION
						);
						fprintf(
							stdout, 
							"Using PL version %s.%s.%s(%s)", 
							PL_VERSION_MAJOR, 
							PL_VERSION_MINOR, 
							PL_VERSION_REVISION, 
							PL_VERSION_OS
						);
						fprintf(stdout, "\n");
						goto parser_done;
						break;

					//---------------------------------------------------------
					// [-G <string>] option.
					//---------------------------------------------------------
					case G_ID:
					case GUID_ID:
						if(i + 1 >= p->argc) {
							ERROR_INDEX(
								"Missing argument after token [%s] in position [%d].", 
								p->argv[i], 
								i + 1
							);
						}
						p->f_guid = 1;
						i++;
						p->esrv_guid = p->argv[i];
						ret = plh_filter_uuid_string(p->esrv_guid);
						if(ret == PL_FAILURE) {
							memset(buffer, 0, sizeof(buffer));
							sprintf(
								buffer, 
								"Guid [%s] does not seem to be a valig guid.", 
								p->esrv_guid
							);
							_ERROR(buffer);
						}
						goto parser_skip;
						break;

					//---------------------------------------------------------
					// [-X <string>] option.
					//---------------------------------------------------------
					case X_ID:
					case GUID_SHELL_VARIABLE_ID:
						if(i + 1 >= p->argc) {
							ERROR_INDEX(
								"Missing argument after token [%s] in position [%d].", 
								p->argv[i], 
								i + 1
							);
						}
						p->f_guid_shell_variable = 1;
						i++;
						p->esrv_guid_shell_variable = p->argv[i];
						p->esrv_guid_shell_variable_value = getenv(p->esrv_guid_shell_variable); 
						if(p->esrv_guid_shell_variable_value == NULL) {
							memset(buffer, 0, sizeof(buffer));
							sprintf(
								buffer, 
								"Environment variable [%s] does not exist.", 
								p->esrv_guid_shell_variable
							);
							_ERROR(buffer);
						} else {
							ret = plh_filter_uuid_string(p->esrv_guid_shell_variable_value);
							if(ret == PL_FAILURE) {
								memset(buffer, 0, sizeof(buffer));
								sprintf(
									buffer, 
									"Environment variable [%s] value [%s] does not seem to be a valig guid.", 
									p->esrv_guid_shell_variable,
									p->esrv_guid_shell_variable_value
								);
								_ERROR(buffer);
							}
						}
						goto parser_skip;
						break;

					//---------------------------------------------------------
					// [-N <integer>] option.
					//---------------------------------------------------------
					case N_ID:
					case CHANNEL_ID:
						if(i + 1 >= p->argc) {
							ERROR_INDEX(
								"Missing argument after token [%s] in position [%d].", 
								p->argv[i], 
								i + 1
							);
						}
						i++;
						p->channel = (unsigned int)atoi(p->argv[i]);
						if(value == 0) {
							value = ENERGY_DEFAULT_CHANNEL;
						}
						p->f_channel = 1;
						goto parser_skip;
						break;

					//---------------------------------------------------------
					// [-E <string>] option.
					//---------------------------------------------------------
					case E_ID:
					case ESRV_OPTIONS_ID:
						if(i + 1 >= p->argc) {
							ERROR_INDEX(
								"Missing argument after token [%s] in position [%d].", 
								p->argv[i], 
								i + 1
							);
						}
						p->f_esrv_options = 1;
						i++;
						p->esrv_options = p->argv[i];
						goto parser_skip;
						break;

					//---------------------------------------------------------
					// -- separator id.
					//---------------------------------------------------------
					case SEPARATOR_ID:
						if(i + 1 >= p->argc) {
							ERROR_INDEX(
								"Missing argument after token [%s] in position [%d].", 
								p->argv[i], 
								i + 1
							);
						}
						i++;
						memset(p->command, 0, sizeof(p->command));
						strncpy(p->command, p->argv[i], strlen(p->argv[i]));
						while(++i < p->argc) {
							strncat(p->command, " ", strlen(" "));
							strncat(p->command, p->argv[i], strlen(p->argv[i]));
						}
						p->f_command = 1;
						goto parser_done;
						break;

					//---------------------------------------------------------
					// Unknown option.
					//---------------------------------------------------------
					default:
						ERROR_INDEX(
							"Unable to make sense of token [%s] in position [%d].", 
							buffer, 
							j
						);
				}
			}
		} // for j
		_ERROR("Unable to make sense of options.  Use --help option for help.");
		goto error;
parser_skip:
		;
	} // for i
parser_done:

	//-------------------------------------------------------------------------
	// Return status.
	//-------------------------------------------------------------------------
	return(PL_SUCCESS);
error:
	return(PL_FAILURE);
}
/*---------------------------------------------------------------------------
Function: main
Purpose : benchmark entry point
In      : none
Out     : none
Return  : status

History
----------------------------------------------------------------------------
Date        : Author                  Modification
----------------------------------------------------------------------------
04/16/2009    Jamel Tayeb             Creation.
*/
int main(void) {

    //----------------------------------------------------------------------
    // timing data
    //----------------------------------------------------------------------
#ifdef __PL_WINDOWS__
    __int64 start_counter = 0;
    __int64 stop_counter = 0;
    __int64 average_counter = 0;
    __int64 base_average_cycles = 0;
    __int64 pl_write_average_cycles = 0;
    __int64 pl_read_average_cycles = 0;
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
    unsigned long long start_counter = 0;
    unsigned long long stop_counter = 0;
    unsigned long long average_counter = 0;
    unsigned long long base_average_cycles = 0;
    unsigned long long pl_write_average_cycles = 0;
    unsigned long long pl_read_average_cycles = 0;
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__

    //----------------------------------------------------------------------
    // Test PL data
    //----------------------------------------------------------------------
    PL_STATUS ret = PL_FAILURE;
    int pld = PL_INVALID_DESCRIPTOR;
    uuid_t uuid;
    char application_name[] = "benchmark";
    const char *counter_names[] = { "test_counter" };
    unsigned int counter_count = 1;
    unsigned long long i = 0;
    unsigned long long x = 0;
    int j = 0;

    //-----------------------------------------------------------------------
    // open test PL
    //-----------------------------------------------------------------------
    fprintf(stdout, "\n");
    fprintf(stdout, "************************************************************\n");
    fprintf(stdout, "* This micro-benchmark uses RDTSC / _IA64_REG_AR_ITC to    *\n");
    fprintf(stdout, "* evaluate the processor cycles required to carry-out the  *\n");
    fprintf(stdout, "* pl_write() and pl_read() Productivity Link API calls     *\n");
#ifdef __PL_WINDOWS__
    fprintf(stdout, "* (%12I64u write and read operations are performed    *\n", (unsigned long long)(MAX_ITERATIONS * MAX_OPERATIONS));
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
    fprintf(stdout, "* (%12llu write and read operations are performed    *\n", (unsigned long long)(MAX_ITERATIONS * MAX_OPERATIONS));
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
    fprintf(stdout, "* in sequence).                                            *\n");
    fprintf(stdout, "*                                                          *\n");
    fprintf(stdout, "* This method is imperfect as it will catch cycles         *\n");
    fprintf(stdout, "* consumed by interrupt routines.  Therefore, consider     *\n");
    fprintf(stdout, "* numbers as indications, not exact values.                *\n");
    fprintf(stdout, "* When running the benchmark, stop all other applications. *\n");
    fprintf(stdout, "*                                                          *\n");
    fprintf(stdout, "* When compiling the benchmark, deactivate all compiler    *\n");
    fprintf(stdout, "* optimizations.  Run in single processor configuration.   *\n");
    fprintf(stdout, "* Deactivate all frequency and voltage adjustment          *\n");
    fprintf(stdout, "* features before running this micro-benchmark.            *\n");
    fprintf(stdout, "************************************************************\n");
    fprintf(stdout, "\n");

    fprintf(stdout, "Opening Test PL:\n");
    pld = pl_open(application_name, counter_count, counter_names, &uuid);
    assert(pld != PL_INVALID_DESCRIPTOR);

    //-- test zone ----------------------------------------------------------

    /*
    @@@@@    @@    @@@@  @@@@@@         @@@@@ @@@@@@  @@@@   @@@@@
    @    @  @  @  @    @ @                @   @      @    @    @
    @    @ @    @ @      @                @   @      @         @
    @@@@@  @    @  @@@@  @@@@@@           @   @@@@@@  @@@@     @
    @    @ @@@@@@      @ @                @   @           @    @
    @    @ @    @      @ @                @   @           @    @
    @@@@@  @    @ @@@@@  @@@@@@           @   @@@@@@ @@@@@     @
    */
    //-----------------------------------------------------------------------
    // reference loop
    //-----------------------------------------------------------------------
    init_average();
    fprintf(stdout, "Measuring Base Data       : ");
    for(j = 0; j < MAX_ITERATIONS; j++) {
        fprintf(stdout, "*");
        start_timer();
        for(i = 0; i < MAX_OPERATIONS; i++) {
            ;
        }
        stop_timer();
        x = i; // consume i in case optimization is activated
        sum_average();
    }
    print_timer();
    fprintf(stdout, "\n");
    compute_average(MAX_ITERATIONS * MAX_OPERATIONS);
    base_average_cycles = average_counter;

    /*
    @    @ @@@@@  @@@@@   @@@@@ @@@@@@         @@@@@ @@@@@@  @@@@   @@@@@
    @    @ @    @   @       @   @                @   @      @    @    @
    @  @ @ @    @   @       @   @                @   @      @         @
    @  @ @ @@@@@    @       @   @@@@@@           @   @@@@@@  @@@@     @
    @  @ @ @  @     @       @   @                @   @           @    @
     @ @ @ @   @    @       @   @                @   @           @    @
      @ @  @    @ @@@@@     @   @@@@@@           @   @@@@@@ @@@@@     @
    */
    //----------------------------------------------------------------------
    // write loop
    //----------------------------------------------------------------------
    init_average();
    fprintf(stdout, "Measuring pl_write() Data : ");
    for(j = 0; j < MAX_ITERATIONS; j++) {
        fprintf(stdout, "*");
        start_timer();
        for(i = 0; i < MAX_OPERATIONS; i++) {
            pl_write(pld, &i, 0);
        }
        stop_timer();
        sum_average();
    }
    print_timer();
    fprintf(stdout, "\n");
    compute_average(MAX_ITERATIONS * MAX_OPERATIONS);
    pl_write_average_cycles = average_counter;

    /*
    @@@@@  @@@@@@   @@   @@@@@          @@@@@ @@@@@@  @@@@   @@@@@
    @    @ @       @  @  @    @           @   @      @    @    @
    @    @ @      @    @ @    @           @   @      @         @
    @@@@@  @@@@@@ @    @ @    @           @   @@@@@@  @@@@     @
    @  @   @      @@@@@@ @    @           @   @           @    @
    @   @  @      @    @ @    @           @   @           @    @
    @    @ @@@@@@ @    @ @@@@@            @   @@@@@@ @@@@@     @
    */
    //----------------------------------------------------------------------
    // read loop
    //----------------------------------------------------------------------
    init_average();
    fprintf(stdout, "Measuring pl_read() Data  : ");
    for(j = 0; j < MAX_ITERATIONS; j++) {
        fprintf(stdout, "*");
        start_timer();
        for(i = 0; i < MAX_OPERATIONS; i++) {
            pl_read(pld, &x, 0);
        }
        stop_timer();
        sum_average();
    }
    print_timer();
    fprintf(stdout, "\n");
    compute_average(MAX_ITERATIONS * MAX_OPERATIONS);
    pl_read_average_cycles = average_counter;

    //-- end test zone -----------------------------------------------------

    //----------------------------------------------------------------------
    // close test PL
    //----------------------------------------------------------------------
    fprintf(stdout, "Closing Test PL.\n");
    ret = pl_close(pld);
    assert(ret == PL_SUCCESS);

    //----------------------------------------------------------------------
    // report test results
    //----------------------------------------------------------------------
    fprintf(stdout, "\n");
    fprintf(stdout, "Test Report:\n");
    fprintf(stdout, "=============\n");
    fprintf(stdout, "\n");
#ifdef __PL_WINDOWS__
    fprintf(stdout, "Average CPU cycles per pl_write() call :       %12I64u.\n", pl_write_average_cycles);
    fprintf(stdout, "Average CPU cycles per pl_read() call  :       %12I64u.\n", pl_read_average_cycles);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
    fprintf(stdout, "Average CPU cycles per pl_write() call :       %12llu.\n", pl_write_average_cycles);
    fprintf(stdout, "Average CPU cycles per pl_read() call  :       %12llu.\n", pl_read_average_cycles);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__
    fprintf(stdout, "\n");
    fprintf(stdout, "Notes:\n");
    fprintf(stdout, "======\n");
    fprintf(stdout, " *  A 2.0 GHz processor with two cores processes ~4 billion\n");
    fprintf(stdout, "instructions per second (~2 billion per core).   Allocation\n");
    fprintf(stdout, "of system CPU cycles under the following conditions:\n");
#ifdef __PL_WINDOWS__
    fprintf(stdout, "1000 pl_write() calls per second:                   ~%1.3f%%\n", (double)pl_write_average_cycles / 40000.0);
    fprintf(stdout, "1000 pl_read() calls per second:                    ~%1.3f%%\n", (double)pl_read_average_cycles / 40000.0);
#endif // __PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
    fprintf(stdout, "1000 pl_write() calls per second:                   ~%1.3f%%\n", (double)pl_write_average_cycles / 40000.0);
    fprintf(stdout, "1000 pl_read() calls per second:                    ~%1.3f%%\n", (double)pl_read_average_cycles / 40000.0);
#endif // __PL_LINUX__ || __PL_SOLARIS__ || __PL_MACOSX__	fprintf(stdout, "\n");
    fprintf(stdout, "\n");
    fprintf(stdout, " *  1.0 GHz cycle = 1.0 nanosecond = 1.0 x 10-9 second.\n");
    fprintf(stdout, "                10 nanoseconds   =   1.0 x 10-5 millisecond.\n");
    fprintf(stdout, "               100 nanoseconds   =       0.0001 millisecond.\n");
    fprintf(stdout, "             1,000 nanoseconds   =        0.001 millisecond.\n");
    fprintf(stdout, "            10,000 nanoseconds   =         0.01 millisecond.\n");
    fprintf(stdout, "          100,000 nanoseconds    =          0.1 millisecond.\n");
    fprintf(stdout, "        1,000,000 nanoseconds    =            1 millisecond.\n");
    fprintf(stdout, "\n");
    fprintf(stdout, "************************************************************\n");
    fprintf(stdout, "*               End of micro-benchmark                     *\n");
    fprintf(stdout, "************************************************************\n");
    fprintf(stdout, "\n");

    return(PL_SUCCESS);
}
/*---------------------------------------------------------------------------
Function: main
Purpose : statistic collector entry point
In      : none
Out     : none
Return  : status

History
----------------------------------------------------------------------------
Date        : Author                  Modification
----------------------------------------------------------------------------
07/08/2009    Jamel Tayeb             Creation.
*/
int main(void) {

	int f_load = 0;
	int f_cpu = 0;
	int bret = -1;
	ssize_t sret = 0;
	char separators_load[] = " /\t\n";
	char separators_cpu[] = " \t\n";
	char buffer[BUFFER_SIZE] = { '\0' };
	char *token = NULL;
	unsigned long long value = 0;

	struct sigaction sa;

	char *startup[STARTUP_MESSAGE_LINES_COUNT] = { STARTUP_MESSAGE_LINES };
	unsigned long long samples = 0;
	int chars_displayed = 0;
	int i = 0;

	unsigned long long user_last = 0;
	unsigned long long nice_last = 0;
	unsigned long long system_last = 0;
	unsigned long long idle_last = 0;
	unsigned long long io_wait_last = 0;
	unsigned long long soft_irq_last = 0;
	unsigned long long hard_irq_last = 0;
	unsigned long long steal_last = 0;
	unsigned long long user = 0;
	unsigned long long nice = 0;
	unsigned long long system = 0;
	unsigned long long idle = 0;
	unsigned long long io_wait = 0;
	unsigned long long soft_irq = 0;
	unsigned long long hard_irq = 0;
	unsigned long long steal = 0;

	unsigned long long usage_percentage = 0;
	long double usage_time = 0;
	long double total_time = 0;

	PL_STATUS ret = PL_FAILURE;
	int pld = PL_INVALID_DESCRIPTOR;
	uuid_t uuid;
	char application_name[] = APPLICATION_NAME;

	const char *counters_names[COUNTERS_COUNT] = { 
		CPU_USAGE_COUNTERS, 
		LOAD_COUNTERS, 
		CPU_COUNTERS, 
	};

	enum counters_indexes { 
		CPU_USAGE_PERCENTAGE_INDEX = 0, 
		CPU_USAGE_PERCENTAGE_DECIMALS_INDEX,

		CPU_AND_IO_UTILIZATION_OF_THE_LAST_MINUTE_PERIOD_INDEX, 
		CPU_AND_IO_UTILIZATION_OF_THE_LAST_MINUTE_PERIOD_DECIMALS_INDEX,
		CPU_AND_IO_UTILIZATION_OF_THE_LAST_FIVE_MINUTES_PERIOD_INDEX, 
		CPU_AND_IO_UTILIZATION_OF_THE_LAST_FIVE_MINUTES_PERIOD_DECIMALS_INDEX, 
		CPU_AND_IO_UTILIZATION_OF_THE_LAST_TEN_MINUTES_PERIOD_INDEX, 
		CPU_AND_IO_UTILIZATION_OF_THE_LAST_TEN_MINUTES_PERIOD_DECIMALS_INDEX,
		NUMBER_OF_CURRENTLY_RUNNING_PROCESSES_INDEX, 
		TOTAL_NUMBER_OF_PROCESSES_INDEX, 
		LAST_PROCESS_ID_USED_INDEX,
		
		JIFFIES_IN_USER_MODE_INDEX,
		JIFFIES_IN_LOW_PRIORITY_USER_MODE_INDEX, 
		JIFFIES_IN_SYSTEM_MODE_MODE_INDEX,
		JIFFIES_IN_IDLE_TASK_MODE_INDEX, 
		JIFFIES_IN_IO_WAIT_INDEX, 
		JIFFIES_IN_HARDIRQ_INDEX,
		JIFFIES_IN_SOFTIRQ_INDEX, 
		JIFFIES_IN_STEAL_INDEX
	};

	//------------------------------------------------------------------------
	// print startup message to stdout
	//------------------------------------------------------------------------
	for(i = 0; i < STARTUP_MESSAGE_LINES_COUNT; i++) {
		fprintf(stdout, "%s", startup[i]);
	}

	//------------------------------------------------------------------------
	// instal signal handler
	//------------------------------------------------------------------------
	sa.sa_handler = signal_handler;
	sigemptyset(&sa.sa_mask);	
	sa.sa_flags = 0;
	bret = sigaction(SIGINT, &sa, NULL);
	assert(bret != -1);

	//------------------------------------------------------------------------
	// open PL
	//------------------------------------------------------------------------
	pld = pl_open(application_name, COUNTERS_COUNT, counters_names, &uuid);
	assert(pld != PL_INVALID_DESCRIPTOR);

	//------------------------------------------------------------------------
	// print PL and use message to stdout
	//------------------------------------------------------------------------
	memset(buffer, 0, sizeof(buffer));
	uuid_unparse(uuid, buffer);
	fprintf(stdout, "Using PL.........: [%s]\n", buffer);
	fprintf(stdout, "Exporting........:\n");
	for(i = 0; i < COUNTERS_COUNT; i++) {
		fprintf(stdout, ".................: [%s]\n", counters_names[i]);
	}
	fprintf(stdout, "To Stop Logging  : [<CRTL>+<C>]\n");

	//------------------------------------------------------------------------
	// write static PL counters
	//------------------------------------------------------------------------
	value = 2;
	ret = pl_write(pld, &value, CPU_USAGE_PERCENTAGE_DECIMALS_INDEX); ASSERT;
	ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_MINUTE_PERIOD_DECIMALS_INDEX); ASSERT;
	ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_FIVE_MINUTES_PERIOD_DECIMALS_INDEX); ASSERT;
	ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_TEN_MINUTES_PERIOD_DECIMALS_INDEX); ASSERT;

	//------------------------------------------------------------------------
	// read and export data every second
	//------------------------------------------------------------------------
	while(stop == 0) {

		//--------------------------------------------------------------------
		// start with load data
		//--------------------------------------------------------------------
		f_load = open(DATA_SOURCE_LOAD, O_RDONLY);
		assert(f_load != -1);
		memset(buffer, 0,  BUFFER_SIZE);
		sret = read(f_load, buffer, BUFFER_SIZE);
		assert(sret != -1);
		// last minute load
		token = strtok(buffer, separators_load);
		if(token != NULL) {		
			value = (unsigned long long)(atof(token) * SCALING);
			ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_MINUTE_PERIOD_INDEX); ASSERT;
		}
		// last five minutes load
		token = strtok(NULL, separators_load);
		if(token != NULL) {				
			value = (unsigned long long)(atof(token) * SCALING);
			ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_FIVE_MINUTES_PERIOD_INDEX); ASSERT;
		}
		// last ten minutes load
		token = strtok(NULL, separators_load);
		if(token != NULL) {				
			value = (unsigned long long)(atof(token) * SCALING);
			ret = pl_write(pld, &value, CPU_AND_IO_UTILIZATION_OF_THE_LAST_TEN_MINUTES_PERIOD_INDEX); ASSERT;
		}
		// running processes
		token = strtok(NULL, separators_load);
		if(token != NULL) {				
			value = (unsigned long long)atoi(token);
			ret = pl_write(pld, &value, NUMBER_OF_CURRENTLY_RUNNING_PROCESSES_INDEX); ASSERT;
		}
		// total number of precesses
		token = strtok(NULL, separators_load);
		if(token != NULL) {				
			value = (unsigned long long)atoi(token);
			ret = pl_write(pld, &value, TOTAL_NUMBER_OF_PROCESSES_INDEX); ASSERT;
		}
		// last process id used
		token = strtok(NULL, separators_load);
		if(token != NULL) {				
			value = (unsigned long long)atoi(token);
			ret = pl_write(pld, &value, LAST_PROCESS_ID_USED_INDEX); ASSERT;
		}
		bret = close(f_load);
		assert(bret != -1);

		//--------------------------------------------------------------------
		// continue with cpu data
		//--------------------------------------------------------------------
		f_cpu = open(DATA_SOURCE_CPU, O_RDONLY);
		assert(f_cpu != -1);
		memset(buffer, 0,  BUFFER_SIZE);
		sret = read(f_cpu, buffer, BUFFER_SIZE);
		assert(sret != -1);
		token = strtok(buffer, separators_cpu);
		while(token != NULL) {
			if(strcmp(token, CPU_TO_MONITOR) == 0) {
				// jiffies in user mode
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_USER_MODE_INDEX); ASSERT;
					user_last = user;
					user = value;
				}
				// jiffies in low priority user mode - nice
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_LOW_PRIORITY_USER_MODE_INDEX); ASSERT;
					nice_last = nice;
					nice = value;
				}
				// jiffies in system mode mode
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_SYSTEM_MODE_MODE_INDEX); ASSERT;
					system_last = system;
					system = value;
				}
				// jiffies in idle task mode
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_IDLE_TASK_MODE_INDEX); ASSERT;
					idle_last = idle;
					idle = value;
				}
				// jiffies in IO wait
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_IO_WAIT_INDEX); ASSERT;
					io_wait_last = io_wait;
					io_wait = value;
				}
				// jiffies in IRQ - hardirq
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_HARDIRQ_INDEX); ASSERT;
					hard_irq_last = hard_irq;
					hard_irq = value;
				}
				// jiffies in IRQ - softirq
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_SOFTIRQ_INDEX); ASSERT;
					soft_irq_last = soft_irq;
					soft_irq = value;
				}
				// jiffies in steal
				token = strtok(NULL, separators_cpu);
				if(token != NULL) {				
					value = (unsigned long long)atoi(token);
					ret = pl_write(pld, &value, JIFFIES_IN_STEAL_INDEX); ASSERT;
					steal_last = steal;
					steal = value;
				}
				goto found;
			}
			token = strtok(NULL, separators_cpu);
		}
found:
		bret = close(f_cpu);
		assert(bret != -1);

		//-------------------------------------------------------------------
		// compute cpu utilization %
		//-------------------------------------------------------------------
		usage_time = (long double)(
			(user - user_last) + 
			(nice - nice_last) + 
			(system - system_last) + 
			(soft_irq - soft_irq_last) +
			(hard_irq - hard_irq_last) +
			(steal - steal_last)
		);
		total_time = (long double)(
			usage_time + 
			(idle - idle_last) +
			(io_wait - io_wait_last)
		);
		usage_percentage = (unsigned long long)(100.0 * usage_time / total_time) * 100;
		ret = pl_write(pld, &usage_percentage, CPU_USAGE_PERCENTAGE_INDEX); ASSERT;

		//-------------------------------------------------------------------
		// increment sample(s) count
		//-------------------------------------------------------------------
		samples++;

		//-------------------------------------------------------------------
		// print to stdout the sample count
		//-------------------------------------------------------------------
		chars_displayed = fprintf(stdout, "Samples          : [%llu]", samples);
		fflush(stdout);
		for(i = 0; i < chars_displayed; i++) { fprintf(stdout, "\b"); }

		//-------------------------------------------------------------------
		// sleep for a second
		//-------------------------------------------------------------------
		sleep(1);
	}

	//------------------------------------------------------------------------
	// close PL
	//------------------------------------------------------------------------
	ret = pl_close(pld); ASSERT;
	return(PL_SUCCESS);
}