Re: Fwd: [PATCH] operf main process improperly killing conversion process

Maynard Johnson <[email protected]>
Newsgroups gmane.linux.oprofile
Message-ID <[email protected]>
On 08/12/2014 10:12 AM, Andrew Haley wrote:
> On 08/06/2014 02:59 PM, Maynard Johnson wrote:
>> Andrew, just to be clear . . . it's the patch below that I want you to test.
> 
> This patch seems to work fine.  With JIT dum enabled, the tail of the
> output looks like:
Thanks for testing, Andrew.

Patch committed.

-Maynard
> 
> section = .text.9298, i = 9314, code = 7fb7d16f89, vma = 7f9da735c0, offset = 6600,size = 384, name = Lorg/eclipse/osgi/baseadaptor/bundlefile/MRUBundleFileList;removeInternal(Lorg/eclipse/osgi/baseadaptor/bundlefile/BundleFile;)V
> section = .text.9298, i = 9315, code = 7fb7da44c8, vma = 7f9da73c40, offset = 6c80,size = 128, name = Lorg/osgi/framework/Version;getMajor()I
> section = .text.9298, i = 9316, code = 7fb7d962b0, vma = 7f9da73f40, offset = 6f80,size = 1024, name = Lorg/eclipse/osgi/internal/resolver/StateWriter;getFromObjectTable(Ljava/lang/Object;)I
> section = .text.9298, i = 9317, code = 7fb7d8f9d5, vma = 7f9da74c00, offset = 7c40,size = 16000, name = Lorg/eclipse/osgi/internal/resolver/StateWriter;writeMap(Ljava/io/DataOutputStream;Ljava/util/Map;)V~1
> section = .text.9318, i = 9318, code = 7fb7d197ac, vma = 7f9da7aec0, offset = 0,size = 21248, name = Lorg/openide/util/lookup/InheritanceTree;classToNode(Lorg/openide/util/lookup/InheritanceTree$Node;Ljava/lang/Class;)Lorg/openide/util/lookup/InheritanceTree$Node;
> section = .text.9319, i = 9319, code = 7fb7e034f9, vma = 7f9da8db00, offset = 0,size = 20032, name = Lorg/netbeans/ProxyClassLoader;getResourceImpl(Ljava/lang/String;)Ljava/net/URL;
> JIT dump processing complete.
> operf-read process returned OK
> 
> Profiling done.
> 
> Thanks, and sorry for the delay.
> 
> Andrew.
> 
> 
>> -------- Original Message --------
>> Subject: [PATCH] operf main process improperly killing conversion process
>> Date: Thu, 31 Jul 2014 17:34:35 -0500
>> From: Maynard Johnson <[email protected]>
>> To: oprofile-list <[email protected]>,        Andrew Haley <[email protected]>
>> CC: William Cohen <[email protected]>
>>
>> operf main process improperly killing conversion process
>>
>> Unless operf is run with --lazy-conversion, a separate process (the
>> 'operf_read_pid' as referred to in the code) is forked to read
>> perf_events-formatted kernel data from a pipe.  The pipe writer is
>> another oprofile process  -- operf_record_pid -- which is reading the
>> actual kernel data.  The operf_read_pid converts this data to oprofile
>> sample files.  This conversion process is fairly expensive, so it's not
>> unusual for it to take a little while after profiling stops for it to
>> complete.  And the more samples there are (i.e., the longer the profile runs),
>> the longer while it will take.  When profiling stops, we call
>> _kill_operf_read_pid and a 5 second timeout begins, waiting for operf_read_pid
>> to finish its conversion. If it has not completed in 5 seconds, we kill the
>> operf_convert_pid!
>>
>> Having a hard-coded timeout was a bad idea; we should always allow the
>> conversion process to finish. It should eventually read all the data from the
>> pipe and then detect that the write has closed the pipe.  At that point,
>> it will exit on its own.
>>
>> This patch changes the name of the _kill_operf_read_pid() function to
>> _waitfor_operf_read_pid(), and modifies its behavior to simply wait for
>> the read process to finish and check its exit status.  The conversion
>> process has been slightly modified so that, when it's in post-profiling
>> conversion phase, it will print a "." every 1 million records processed
>> to show its progress.
>>
>> Signed-off-by: Maynard Johnson <[email protected]>
>> Reported-by:   Andrew Haley <[email protected]>
>> ---
>>  libperf_events/operf_counter.cpp |   33 +++++++++---
>>  libperf_events/operf_counter.h   |    4 +-
>>  pe_profiling/operf.cpp           |  112 +++++++++++++++-----------------------
>>  3 files changed, 73 insertions(+), 76 deletions(-)
>>
>> diff --git a/libperf_events/operf_counter.cpp b/libperf_events/operf_counter.cpp
>> index 936d612..88e1708 100644
>> --- a/libperf_events/operf_counter.cpp
>> +++ b/libperf_events/operf_counter.cpp
>> @@ -60,6 +60,15 @@ static const char *__op_magic = "OPFILE";
>>
>>  #define OP_MAGIC	(*(u64 *)__op_magic)
>>
>> +static bool _print_pp_progress(int fd)
>> +{
>> +	int msg;
>> +	if (read(fd, &msg, sizeof(msg)) > 0)
>> +		return true;
>> +	else
>> +		return false;
>> +}
>> +
>>  /* This function for reading an event from the sample data pipe must
>>   * be robust enough to handle the situation where the operf_record process
>>   * writes an event record to the pipe in multiple chunks.
>> @@ -868,11 +877,13 @@ void operf_record::recordPerfData(void)
>>  }
>>
>>  void operf_read::init(int sample_data_pipe_fd, string input_filename, string samples_loc, op_cpu cputype,
>> -                      bool systemwide, int _record_write_pipe, int _record_read_pipe)
>> +                      bool systemwide, int _record_write_pipe, int _record_read_pipe,
>> +                      int _post_profiling_pipe)
>>  {
>>  	sample_data_fd = sample_data_pipe_fd;
>>  	read_comm_pipe = _record_read_pipe;
>>  	write_comm_pipe = _record_write_pipe;
>> +	post_profiling_pipe = _post_profiling_pipe;
>>  	inputFname = input_filename;
>>  	sampledir = samples_loc;
>>  	cpu_type = cputype;
>> @@ -1079,6 +1090,11 @@ unsigned int operf_read::convertPerfData(void)
>>  	bool error = false;
>>  	event_t * event = NULL;
>>
>> +	if (fcntl(post_profiling_pipe, F_SETFL, O_NONBLOCK) < 0) {
>> +		cerr << "Error: fcntl failed with errno:\n\t" << strerror(errno) << endl;
>> +		throw runtime_error("Error: Unable to set post_profiling_pipe to non blocking");
>> +	}
>> +
>>  	if (!inputFname.empty()) {
>>  		info.file_data_offset = opHeader.data_offset;
>>  		info.file_data_size = opHeader.data_size;
>> @@ -1112,9 +1128,8 @@ unsigned int operf_read::convertPerfData(void)
>>  	first_time_processing = true;
>>  	int num_recs = 0;
>>  	struct perf_event_header last_header;
>> -	bool print_progress = !inputFname.empty() && syswide;
>> -	if (print_progress)
>> -		cerr << "Converting profile data to OProfile format" << endl;
>> +        bool print_progress = !inputFname.empty() && syswide;
>> +        bool printed_progress_msg = false;
>>  	while (1) {
>>  		streamsize rec_size = 0;
>>  		if (!inputFname.empty()) {
>> @@ -1135,10 +1150,14 @@ unsigned int operf_read::convertPerfData(void)
>>  		}
>>  		num_bytes += rec_size;
>>  		num_recs++;
>> -		if ((num_recs % 1000000 == 0) && print_progress)
>> +		if ((num_recs % 1000000 == 0) && (print_progress || _print_pp_progress(post_profiling_pipe))) {
>> +			if (!printed_progress_msg) {
>> +				cerr << "\nConverting profile data to OProfile format " << endl;
>> +				printed_progress_msg = true;
>> +			}
>>  			cerr << ".";
>> +		}
>>  	}
>> -
>>  	if (unlikely(error)) {
>>  		if (!inputFname.empty()) {
>>  			cerr << "ERROR: operf_read::convertPerfData quitting. Bad data read from file." << endl;
>> @@ -1156,7 +1175,7 @@ unsigned int operf_read::convertPerfData(void)
>>  	if (!error)
>>  		op_reprocess_unresolved_events(opHeader.h_attrs[0].attr.sample_type, print_progress);
>>
>> -	if (print_progress)
>> +	if (printed_progress_msg)
>>  		cerr << endl;
>>
>>  	op_release_resources();
>> diff --git a/libperf_events/operf_counter.h b/libperf_events/operf_counter.h
>> index dfb4336..70e7aac 100644
>> --- a/libperf_events/operf_counter.h
>> +++ b/libperf_events/operf_counter.h
>> @@ -139,7 +139,8 @@ public:
>>  	: sample_data_fd(-1), inputFname(""), evts(_evts), cpu_type(CPU_NO_GOOD)
>>  	  { valid = syswide = false;}
>>  	void init(int sample_data_pipe_fd, std::string input_filename, std::string samples_dir, op_cpu cputype,
>> -	          bool systemwide, int _record_write_pipe, int _record_read_pipe);
>> +	          bool systemwide, int _record_write_pipe, int _record_read_pipe,
>> +	          int _post_profiling_pipe);
>>  	~operf_read();
>>  	int readPerfHeader(void);
>>  	unsigned int convertPerfData(void);
>> @@ -154,6 +155,7 @@ private:
>>  	int sample_data_fd;
>>  	int write_comm_pipe;
>>  	int read_comm_pipe;
>> +	int post_profiling_pipe;
>>  	std::string inputFname;
>>  	std::string sampledir;
>>  	std::ifstream istrm;
>> diff --git a/pe_profiling/operf.cpp b/pe_profiling/operf.cpp
>> index 51f55a5..668f9ea 100644
>> --- a/pe_profiling/operf.cpp
>> +++ b/pe_profiling/operf.cpp
>> @@ -110,6 +110,10 @@ static int operf_convert_record_write_pipe[2];
>>  // The operf_record_convert_write_pipe is used for the record process to send
>>  // data to the convert process in response to the forked PID data.
>>  static int operf_record_convert_write_pipe[2];
>> +// The operf_post_profiling_pipe is used by the main process to inform the operf_read_pid
>> +// that profiling is done.  The operf_read_pid will then print its progress in
>> +// finishing the conversion.
>> +static int operf_post_profiling_pipe[2];
>>
>>  bool ctl_c = false;
>>  bool pipe_closed = false;
>> @@ -472,81 +476,47 @@ fail_out:
>>  	return 0;
>>  }
>>
>> -static end_code_t _kill_operf_read_pid(end_code_t rc)
>> +static end_code_t _waitfor_operf_read_pid(end_code_t rc)
>>  {
>> -	// Now stop the operf-read process
>> -	int waitpid_status;
>> -	struct timeval tv;
>> -	long long start_time_sec;
>> -	long long usec_timer;
>> -	bool keep_trying = true;
>> -	waitpid_status = 0;
>> -	gettimeofday(&tv, NULL);
>> -	start_time_sec = tv.tv_sec;
>> -	usec_timer = tv.tv_usec;
>> -	/* We'll initially try the waitpid with WNOHANG once every 100,000 usecs.
>> -	 * If it hasn't ended within 5 seconds, we'll kill it and do one
>> -	 * final wait.
>> -	 */
>> -	while (keep_trying) {
>> -		int option = WNOHANG;
>> -		int wait_rc;
>> -		gettimeofday(&tv, NULL);
>> -		if (tv.tv_sec > start_time_sec + 5) {
>> -			keep_trying = false;
>> -			option = 0;
>> -			cerr << "now trying to kill convert pid..." << endl;
>> +	// Now wait for the operf-read process to finish
>> +	int wait_rc, waitpid_status, post_processing = 1;
>>
>> -			if (kill(operf_read_pid, SIGUSR1) < 0) {
>> -				perror("Attempt to stop operf-read process failed");
>> -				rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>> -				break;
>> -			}
>> -		} else {
>> -			/* If we exceed the 100000 usec interval or if the tv_usec
>> -			 * value has rolled over to restart at 0, then we reset
>> -			 * the usec_timer to current tv_usec and try waitpid.
>> -			 */
>> -			if ((tv.tv_usec % 1000000) > (usec_timer + 100000)
>> -					|| (tv.tv_usec < usec_timer))
>> -				usec_timer = tv.tv_usec;
>> -			else
>> -				continue;
>> +	if (write(operf_post_profiling_pipe[1], &post_processing, sizeof(post_processing)) < 0) {
>> +		perror("Internal error:  Failed to write to operf_post_profiling_pipe");
>> +		rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>> +		return rc;
>> +	}
>> +	waitpid_status = 0;
>> +	if ((wait_rc = waitpid(operf_read_pid, &waitpid_status, 0)) < 0) {
>> +		if (errno != ECHILD) {
>> +			perror("waitpid for operf-read process failed");
>> +			rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>>  		}
>> -		if ((wait_rc = waitpid(operf_read_pid, &waitpid_status, option)) < 0) {
>> -			keep_trying = false;
>> -			if (errno != ECHILD) {
>> -				perror("waitpid for operf-read process failed");
>> -				rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>> -			}
>> -		} else if (wait_rc) {
>> -			if (WIFEXITED(waitpid_status)) {
>> -				keep_trying = false;
>> -				if (!WEXITSTATUS(waitpid_status)) {
>> -					cverb << vdebug << "operf-read process returned OK" << endl;
>> -				} else if (WIFEXITED(waitpid_status)) {
>> -					/* If user did ctl-c, operf-read may get spurious errors, like
>> -					 * broken pipe, etc.  We ignore these unless the user asks for
>> -					 * debug output.
>> -					 */
>> -					if (!ctl_c || cverb << vdebug) {
>> -						cerr <<  "operf-read process ended abnormally.  Status = "
>> -						     << WEXITSTATUS(waitpid_status) << endl;
>> -						rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>> -					}
>> -				}
>> -			}  else if (WIFSIGNALED(waitpid_status)) {
>> -				keep_trying = false;
>> +	} else if (wait_rc) {
>> +		if (WIFEXITED(waitpid_status)) {
>> +			if (!WEXITSTATUS(waitpid_status)) {
>> +				cverb << vdebug << "operf-read process returned OK" << endl;
>> +			} else {
>>  				/* If user did ctl-c, operf-read may get spurious errors, like
>>  				 * broken pipe, etc.  We ignore these unless the user asks for
>>  				 * debug output.
>>  				 */
>>  				if (!ctl_c || cverb << vdebug) {
>> -					cerr << "operf-read process killed by signal "
>> -					     << WTERMSIG(waitpid_status) << endl;
>> -					rc = PERF_RECORD_ERROR;
>> +					cerr <<  "operf-read process ended abnormally.  Status = "
>> +							<< WEXITSTATUS(waitpid_status) << endl;
>> +					rc = rc ? PERF_BOTH_ERROR : PERF_READ_ERROR;
>>  				}
>>  			}
>> +		}  else if (WIFSIGNALED(waitpid_status)) {
>> +			/* If user did ctl-c, operf-read may get spurious errors, like
>> +			 * broken pipe, etc.  We ignore these unless the user asks for
>> +			 * debug output.
>> +			 */
>> +			if (!ctl_c || cverb << vdebug) {
>> +				cerr << "operf-read process killed by signal "
>> +						<< WTERMSIG(waitpid_status) << endl;
>> +				rc = PERF_RECORD_ERROR;
>> +			}
>>  		}
>>  	}
>>  	return rc;
>> @@ -630,12 +600,17 @@ static end_code_t _run(void)
>>  	if (!operf_options::post_conversion) {
>>  		if (!(!app_started && !operf_options::system_wide)) {
>>  			cverb << vdebug << "Forking read pid" << endl;
>> +			if (pipe(operf_post_profiling_pipe) < 0) {
>> +				perror("Internal error: operf-record could not create pipe");
>> +				_exit(EXIT_FAILURE);
>> +			}
>>  			operf_read_pid = fork();
>>  			if (operf_read_pid < 0) {
>>  				perror("Internal error: fork failed");
>>  				_exit(EXIT_FAILURE);
>>  			} else if (operf_read_pid == 0) { // child process
>>  				close(sample_data_pipe[1]);
>> +				close(operf_post_profiling_pipe[1]);
>>  				_set_basic_SIGINT_handler_for_child();
>>  				convert_sample_data();
>>  				_exit(EXIT_SUCCESS);
>> @@ -647,6 +622,7 @@ static end_code_t _run(void)
>>  			close(operf_convert_record_write_pipe[1]);
>>  			close(operf_record_convert_write_pipe[0]);
>>  			close(operf_record_convert_write_pipe[1]);
>> +			close(operf_post_profiling_pipe[0]);
>>  		}
>>  	}
>>
>> @@ -753,10 +729,10 @@ again:
>>  		if (operf_options::post_conversion)
>>  			rc = _kill_operf_record_pid();
>>  		else
>> -			rc = _kill_operf_read_pid(_kill_operf_record_pid());
>> +			rc = _waitfor_operf_read_pid(_kill_operf_record_pid());
>>  	} else {
>>  		if (!operf_options::post_conversion)
>> -			rc = _kill_operf_read_pid(rc);
>> +			rc = _waitfor_operf_read_pid(rc);
>>  	}
>>
>>  	return rc;
>> @@ -945,7 +921,7 @@ static void convert_sample_data(void)
>>  	close(operf_convert_record_write_pipe[0]);
>>  	operfRead.init(inputfd, inputfname, current_sampledir, cpu_type,
>>  	               operf_options::system_wide, operf_convert_record_write_pipe[1],
>> -	               operf_record_convert_write_pipe[0]);
>> +	               operf_record_convert_write_pipe[0], operf_post_profiling_pipe[0]);
>>  	if ((rc = operfRead.readPerfHeader()) < 0) {
>>  		if (rc != OP_PERF_HANDLED_ERROR)
>>  			cerr << "Error: Cannot create read header info for sample data " << endl;
>>
> 


------------------------------------------------------------------------------
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.