Line data Source code
1 : /**
2 : * @file HDF5RecoverFile.cpp
3 : *
4 : * Preliminary utility to recover a raw data file that did not get closed properly.
5 : *
6 : * This is part of the DUNE DAQ Software Suite, copyright 2024.
7 : * Licensing/copyright details are in the COPYING file that you should have
8 : * received with this code.
9 : */
10 :
11 : #include "hdf5libs/HDF5RawDataFile.hpp"
12 :
13 : #include "daqdataformats/Fragment.hpp"
14 : #include "daqdataformats/SourceID.hpp"
15 :
16 : #include <string>
17 : #include <sys/stat.h>
18 : #include <time.h>
19 : #include <unistd.h>
20 :
21 : using namespace dunedaq::hdf5libs;
22 : using namespace dunedaq::daqdataformats;
23 :
24 : void
25 0 : print_usage(const char* appname)
26 : {
27 0 : std::cout << "Usage: " << appname << " [-R] <file_name>" << std::endl;
28 0 : std::cout << "The default behavior is to simply print out the changes that need to be made." << std::endl;
29 0 : std::cout << "The -R option causes the file to be modifed (Recovered)." << std::endl;
30 0 : }
31 :
32 : int
33 0 : main(int argc, char** argv)
34 : {
35 0 : bool do_recovery = false;
36 0 : signed char opt;
37 0 : while ((opt = getopt(argc, argv, "hR")) != -1) {
38 0 : switch (opt) {
39 0 : case 'h':
40 0 : print_usage(argv[0]);
41 : return 1;
42 : case 'R':
43 : do_recovery = true;
44 : break;
45 0 : default: /* '?' */
46 0 : print_usage(argv[0]);
47 : return 1;
48 : }
49 : }
50 :
51 0 : argc -= (optind - 1);
52 0 : argv += (optind - 1);
53 0 : if (argc != 2) {
54 0 : print_usage(argv[0]);
55 0 : return 1;
56 : }
57 :
58 0 : const std::string ifile_name = std::string(argv[1]);
59 :
60 : // open the file for reading, initially
61 0 : std::unique_ptr<HDF5RawDataFile> h5file_ptr(new HDF5RawDataFile(ifile_name, false));
62 :
63 : // determine a reasonable value for the file-closing time based on the Linux
64 : // file system last-modified time
65 0 : size_t last_modified_time = 0;
66 0 : struct stat stat_results;
67 0 : auto retcode = stat(ifile_name.c_str(), &stat_results);
68 0 : if (retcode == 0) {
69 0 : last_modified_time = 1000 * stat_results.st_mtime;
70 : } // msec
71 :
72 : // determine a reasonable value for the size of the recorded data in the file by
73 : // looping through all of the records and fragments
74 0 : size_t calculated_recorded_size = 0;
75 0 : auto records = h5file_ptr->get_all_record_ids();
76 0 : for (auto const& record_id : records) {
77 0 : if (h5file_ptr->is_timeslice_type()) {
78 0 : try {
79 0 : auto tsh_ptr = h5file_ptr->get_tsh_ptr(record_id);
80 0 : calculated_recorded_size += sizeof(*tsh_ptr);
81 0 : } catch (std::exception const& excpt) {
82 : // bad record header, we'll skip the whole TimeSlice
83 0 : continue;
84 0 : }
85 : } else {
86 0 : try {
87 0 : auto trh_ptr = h5file_ptr->get_trh_ptr(record_id);
88 0 : calculated_recorded_size += trh_ptr->get_total_size_bytes();
89 0 : } catch (std::exception const& excpt) {
90 : // bad record header, we'll skip the whole TriggerRecord
91 0 : continue;
92 0 : }
93 : }
94 0 : std::set<SourceID> frag_sid_list = h5file_ptr->get_fragment_source_ids(record_id);
95 0 : for (auto const& source_id : frag_sid_list) {
96 0 : try {
97 0 : auto frag_ptr = h5file_ptr->get_frag_ptr(record_id, source_id);
98 0 : calculated_recorded_size += frag_ptr->get_size();
99 0 : } catch (std::exception const& excpt) {
100 : // nothing to do, just leave this fragment out of the sum
101 0 : }
102 : }
103 0 : }
104 :
105 0 : std::vector<std::string> attr_names = h5file_ptr->get_attribute_names();
106 0 : bool ct_attr_exists = (std::count(attr_names.begin(), attr_names.end(), "closing_timestamp") >= 1);
107 0 : bool rs_attr_exists = (std::count(attr_names.begin(), attr_names.end(), "recorded_size") >= 1);
108 :
109 0 : size_t closing_timestamp = 0;
110 0 : if (ct_attr_exists) {
111 0 : if (h5file_ptr->get_file_layout().get_version() >= 6) {
112 0 : closing_timestamp = h5file_ptr->get_attribute<size_t>("closing_timestamp");
113 : } else {
114 0 : auto clts_string = h5file_ptr->get_attribute<std::string>("closing_timestamp");
115 0 : char* nds = 0;
116 0 : closing_timestamp = strtol(clts_string.c_str(), &nds, 10);
117 0 : }
118 : }
119 0 : size_t recorded_size = SIZE_MAX;
120 0 : if (rs_attr_exists) {
121 0 : recorded_size = h5file_ptr->get_attribute<size_t>("recorded_size");
122 : }
123 :
124 0 : std::cout << std::endl;
125 0 : std::cout << std::endl;
126 0 : std::cout << "========================================" << std::endl;
127 :
128 0 : if (do_recovery) {
129 :
130 : // re-open the file for reading and writing
131 0 : h5file_ptr.reset();
132 0 : h5file_ptr.reset(new HDF5RawDataFile(ifile_name, true));
133 :
134 0 : if (!ct_attr_exists) {
135 0 : if (h5file_ptr->get_file_layout().get_version() >= 6) {
136 0 : std::cout << "Setting the \"closing_timestamp\" Attribute value to " << last_modified_time << "." << std::endl;
137 0 : h5file_ptr->write_attribute("closing_timestamp", last_modified_time);
138 : } else {
139 0 : std::string file_closing_timestamp = std::to_string(last_modified_time);
140 0 : std::cout << "Setting the \"closing_timestamp\" Attribute value to " << file_closing_timestamp << "."
141 0 : << std::endl;
142 0 : h5file_ptr->write_attribute("closing_timestamp", file_closing_timestamp);
143 0 : }
144 : } else {
145 0 : std::cout << "The \"closing_timestamp\" Attribute in the file is currently set to " << closing_timestamp
146 0 : << ", and it will not be over-written." << std::endl;
147 : }
148 :
149 0 : if (!rs_attr_exists) {
150 0 : std::cout << "Setting the \"recorded_size\" Attribute value to " << calculated_recorded_size << "." << std::endl;
151 0 : h5file_ptr->write_attribute("recorded_size", calculated_recorded_size);
152 : } else {
153 0 : std::cout << "The \"recorded_size\" Attribute in the file is currently set to " << recorded_size
154 0 : << ", and it will not be over-written." << std::endl;
155 : }
156 :
157 0 : if (std::count(attr_names.begin(), attr_names.end(), "file_recovery_timestamp") == 0) {
158 0 : int64_t timestamp =
159 0 : std::chrono::duration_cast<std::chrono::milliseconds>(system_clock::now().time_since_epoch()).count();
160 0 : std::cout << "Setting the \"file_recovery_timestamp\" Attribute value to " << timestamp << "." << std::endl;
161 0 : h5file_ptr->write_attribute("file_recovery_timestamp", timestamp);
162 : } else {
163 0 : size_t fr_timestamp = h5file_ptr->get_attribute<size_t>("file_recovery_timestamp");
164 0 : std::cout << "The \"file_recovery_timestamp\" Attribute in the file is currently set to " << fr_timestamp
165 0 : << ", and it will not be over-written." << std::endl;
166 : }
167 :
168 : // the HDF5RawDataFile Destructor will handle the file renaming, if that is needed.
169 :
170 : } else {
171 :
172 0 : if (std::count(attr_names.begin(), attr_names.end(), "creation_timestamp") == 0) {
173 0 : std::cout << "The \"creation_timestamp\" Attribute is *not* currently set in the file." << std::endl;
174 : } else {
175 0 : size_t creation_timestamp = 0;
176 0 : if (h5file_ptr->get_file_layout().get_version() >= 6) {
177 0 : creation_timestamp = h5file_ptr->get_attribute<size_t>("creation_timestamp");
178 : } else {
179 0 : auto crts_string = h5file_ptr->get_attribute<std::string>("creation_timestamp");
180 0 : char* nds = 0;
181 0 : creation_timestamp = strtol(crts_string.c_str(), &nds, 10);
182 0 : }
183 0 : time_t tmp_time = creation_timestamp / 1000;
184 0 : tm* gmtm = gmtime(&tmp_time);
185 0 : std::cout << "The \"creation_timestamp\" Attribute in the file is currently set to " << creation_timestamp << ","
186 0 : << std::endl
187 0 : << " which corresponds to (UTC) " << asctime(gmtm);
188 : }
189 :
190 0 : if (!ct_attr_exists) {
191 0 : std::cout << "The \"closing_timestamp\" Attribute is *not* currently set in the file, and it will be "
192 0 : << std::endl
193 0 : << " set to " << last_modified_time << " if/when the file is recovered." << std::endl;
194 : } else {
195 0 : time_t tmp_time = closing_timestamp / 1000;
196 0 : tm* gmtm = gmtime(&tmp_time);
197 0 : std::cout << "The \"closing_timestamp\" Attribute in the file is currently set to " << closing_timestamp << ","
198 0 : << std::endl
199 0 : << " which corresponds to (UTC) " << asctime(gmtm) // no std::endl needed with asctime()
200 0 : << " (The recalculated closing time is " << last_modified_time << ".)" << std::endl;
201 : }
202 :
203 0 : if (!rs_attr_exists) {
204 0 : std::cout << "The \"recorded_size\" Attribute is *not* currently set in the file, and it will be " << std::endl
205 0 : << " set to " << calculated_recorded_size << " if/when the file is recovered." << std::endl;
206 : } else {
207 0 : std::cout << "The \"recorded_size\" Attribute in the file is currently set to " << recorded_size << "."
208 0 : << std::endl
209 0 : << " (The recalculated recorded size is " << calculated_recorded_size << ".)" << std::endl;
210 : }
211 :
212 0 : if (std::count(attr_names.begin(), attr_names.end(), "file_recovery_timestamp") == 0) {
213 0 : std::cout << "The \"file_recovery_timestamp\" Attribute is *not* currently set in the file." << std::endl;
214 : } else {
215 0 : size_t fr_timestamp = h5file_ptr->get_attribute<size_t>("file_recovery_timestamp");
216 0 : time_t tmp_time = fr_timestamp / 1000;
217 0 : tm* gmtm = gmtime(&tmp_time);
218 0 : std::cout << "The \"file_recovery_timestamp\" Attribute in the file is currently set to " << fr_timestamp << ","
219 0 : << std::endl
220 0 : << " which corresponds to (UTC) " << asctime(gmtm);
221 : }
222 :
223 0 : if (ifile_name.rfind(HDF5RawDataFile::s_inprogress_suffix) != std::string::npos) {
224 0 : std::cout << "The file *does* have the \"" << HDF5RawDataFile::s_inprogress_suffix << "\" suffix, "
225 0 : << "so it will be renamed if/when it is recovered." << std::endl;
226 : } else {
227 0 : std::cout << "The file name does not have the \"" << HDF5RawDataFile::s_inprogress_suffix << "\" suffix, "
228 0 : << "so it will not be renamed." << std::endl;
229 : }
230 : }
231 :
232 0 : return 0;
233 : } // NOLINT
|