1/**
2 * @file oparchive.cpp
3 * Implement oparchive utility
4 *
5 * @remark Copyright 2003, 2004 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author Will Cohen
9 * @author John Levon
10 * @author Philippe Elie
11 */
12
13#include <cstdlib>
14
15#include <iostream>
16#include <fstream>
17#include <cstdlib>
18
19#include <errno.h>
20#include <string.h>
21#include <dirent.h>
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include "op_file.h"
27#include "op_bfd.h"
28#include "op_config.h"
29#include "oparchive_options.h"
30#include "file_manip.h"
31#include "cverb.h"
32#include "image_errors.h"
33#include "string_manip.h"
34#include "locate_images.h"
35
36using namespace std;
37
38namespace {
39
40
41void copy_one_file(image_error err, string const & source, string const & dest)
42{
43	if (!op_file_readable(source))
44		return;
45
46	if (options::list_files) {
47		cout << source << endl;
48		return;
49	}
50
51	if (!copy_file(source, dest) && err == image_ok) {
52		cerr << "can't copy from " << source << " to " << dest
53		     << " cause: " << strerror(errno) << endl;
54	}
55}
56
57void copy_stats(string const & session_samples_dir,
58		string const & archive_path)
59{
60	DIR * dir;
61	struct dirent * dirent;
62	string stats_path;
63
64	stats_path = session_samples_dir + "stats/";
65
66	if (!(dir = opendir(stats_path.c_str()))) {
67		return;
68	}
69
70	string sample_base_dir = session_samples_dir.substr(archive_path.size());
71	string archive_stats = options::outdirectory + sample_base_dir + "stats/";
72	string archive_stats_path = archive_stats + "event_lost_overflow";
73	if (!options::list_files &&
74	    create_path(archive_stats_path.c_str())) {
75		cerr << "Unable to create directory for "
76		     <<	archive_stats << "." << endl;
77		exit (EXIT_FAILURE);
78	}
79
80	copy_one_file(image_ok, stats_path + "/event_lost_overflow", archive_stats_path);
81
82	while ((dirent = readdir(dir))) {
83		int cpu_nr;
84		string path;
85		if (sscanf(dirent->d_name, "cpu%d", &cpu_nr) != 1)
86			continue;
87		path = string(dirent->d_name) + "/" + "sample_lost_overflow";
88		archive_stats_path = archive_stats + path;
89		if (!options::list_files &&
90		    create_path(archive_stats_path.c_str())) {
91			cerr << "Unable to create directory for "
92			     <<	archive_stats_path << "." << endl;
93			exit (EXIT_FAILURE);
94		}
95		copy_one_file(image_ok, stats_path + path, archive_stats_path);
96
97	}
98	closedir(dir);
99
100}
101
102int oparchive(options::spec const & spec)
103{
104	handle_options(spec);
105
106	string archive_path = classes.extra_found_images.get_archive_path();
107
108	/* Check to see if directory can be created */
109	if (!options::list_files && create_path(options::outdirectory.c_str())) {
110		cerr << "Unable to create directory for "
111		     <<	options::outdirectory << "." << endl;
112		exit (EXIT_FAILURE);
113	}
114
115	/* copy over each of the executables and the debuginfo files */
116	list<inverted_profile> iprofiles = invert_profiles(classes);
117
118	report_image_errors(iprofiles, classes.extra_found_images);
119
120	list<inverted_profile>::iterator it = iprofiles.begin();
121	list<inverted_profile>::iterator const end = iprofiles.end();
122
123	cverb << vdebug << "(exe_names)" << endl << endl;
124	for (; it != end; ++it) {
125
126		string exe_name = it->image;
127		image_error error;
128		string real_exe_name =
129			classes.extra_found_images.find_image_path(it->image,
130						  error, true);
131
132		if (error == image_ok)
133			exe_name = classes.extra_found_images.strip_path_prefix(real_exe_name);
134
135		// output name must be identical to the original name, when
136		// using this archive the used fixup will be identical e.g.:
137		// oparchive -p /lib/modules/2.6.42/kernel -o tmp;
138		// opreport  -p /lib/modules/2.6.42/kernel { archive:tmp }
139		string exe_archive_file = options::outdirectory + exe_name;
140
141		// FIXME: hacky
142		if (it->error == image_not_found && is_prefix(exe_name, "anon "))
143			continue;
144
145		cverb << vdebug << real_exe_name << endl;
146		/* Create directory for executable file. */
147		if (!options::list_files &&
148			create_path(exe_archive_file.c_str())) {
149			cerr << "Unable to create directory for "
150			     << exe_archive_file << "." << endl;
151			exit (EXIT_FAILURE);
152		}
153
154		/* Copy actual executable files */
155		copy_one_file(it->error, real_exe_name, exe_archive_file);
156
157		/* If there are any debuginfo files, copy them over.
158		 * Need to copy the debug info file to somewhere we'll
159		 * find it - executable location + "/.debug"
160		 * to avoid overwriting files with the same name. The
161		 * /usr/lib/debug search path is not going to work.
162		 */
163		bfd * ibfd = open_bfd(real_exe_name);
164		if (ibfd) {
165			string dirname = op_dirname(real_exe_name);
166			string debug_filename;
167			if (find_separate_debug_file(ibfd, real_exe_name,
168				debug_filename, classes.extra_found_images)) {
169				/* found something copy it over */
170				string dest_debug_dir = options::outdirectory +
171					dirname + "/.debug/";
172				if (!options::list_files &&
173				    create_dir(dest_debug_dir.c_str())) {
174					cerr << "Unable to create directory: "
175					<< dest_debug_dir << "." << endl;
176					exit (EXIT_FAILURE);
177				}
178
179				string dest_debug = dest_debug_dir +
180					op_basename(debug_filename);
181				copy_one_file(image_ok, debug_filename, dest_debug);
182			}
183			bfd_close(ibfd);
184		}
185	}
186
187	/* copy over each of the sample files */
188	list<string>::iterator sit = sample_files.begin();
189	list<string>::iterator const send = sample_files.end();
190
191	string a_sample_file = *sit;
192	int offset = a_sample_file.find('{');
193	string base_samples_dir = a_sample_file.substr(0, offset);
194	copy_stats(base_samples_dir, archive_path);
195
196	cverb << vdebug << "(sample_names)" << endl << endl;
197
198	for (; sit != send; ++sit) {
199		string sample_name = *sit;
200		/* Get rid of the the archive_path from the name */
201		string sample_base = sample_name.substr(archive_path.size());
202		string sample_archive_file = options::outdirectory + sample_base;
203
204		cverb << vdebug << sample_name << endl;
205		cverb << vdebug << " destp " << sample_archive_file << endl;
206		if (!options::list_files &&
207			create_path(sample_archive_file.c_str())) {
208			cerr << "Unable to create directory for "
209			     <<	sample_archive_file << "." << endl;
210			exit (EXIT_FAILURE);
211		}
212
213		/* Copy over actual sample file. */
214		copy_one_file(image_ok, sample_name, sample_archive_file);
215	}
216
217	/* copy over the <session-dir>/abi file if it exists */
218	string abi_name = string(op_session_dir) + "/abi";
219	copy_one_file(image_ok, archive_path + abi_name,
220	              options::outdirectory + abi_name);
221
222	/* copy over the <session-dir>/samples/oprofiled.log file */
223	string log_name = string(op_samples_dir) + "/oprofiled.log";
224	copy_one_file(image_ok, archive_path + log_name,
225	              options::outdirectory + log_name);
226
227	return 0;
228}
229
230}  // anonymous namespace
231
232
233int main(int argc, char const * argv[])
234{
235	return run_pp_tool(argc, argv, oparchive);
236}
237