Merge ~sylvain-pineau/checkbox-support:usb_scripts_low_mem into checkbox-support:master

Proposed by Sylvain Pineau
Status: Merged
Approved by: Sylvain Pineau
Approved revision: 19b59f5e6f3fcf9cc593f2d0a2b3049fb6c72789
Merged at revision: 4f429e965c29335ff869de199a01b4a441cc18d6
Proposed branch: ~sylvain-pineau/checkbox-support:usb_scripts_low_mem
Merge into: checkbox-support:master
Diff against target: 178 lines (+23/-27)
2 files modified
checkbox_support/scripts/run_watcher.py (+8/-0)
checkbox_support/scripts/usb_read_write.py (+15/-27)
Reviewer Review Type Date Requested Status
Maciej Kisielewski Abstain
Review via email: mp+341304@code.launchpad.net

Description of the change

This MR allows the non udisks2 usb test to run on low memory devices, tested on tillamook where we only have 100M to run checkbox and the storage test.

To post a comment you must log in.
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Looks good, +1.

Two subjective, style-related comments in line.

One general thought I have for the whole usb_read_write.py is that I feel like instead of optimizing size of the "test file" we should generate the data on-the-fly and write it to two pipes, one to compute the hash for reference, and one that will be written to the drive.

Or at least generate the random file in a less memory-hungry way.
The reference md5 computation doesn't have to be done on a written file. First thought is to use hashlib from python, block by block. The real solution, however, is to use precomputed hashes. For any given size the hash can be known statically (there is a hardcoded seed).

To sum up: content of RandomData could be written to the target file on the fly without a need of any huge buffer. If the size is known upfront precomputed hash can be used.

I feel bad about circumventing the real problem in the script instead of fixing it.

review: Abstain
Revision history for this message
Maciej Kisielewski (kissiel) wrote :

> Looks good, +1.
>
> Two subjective, style-related comments in line.
>
> One general thought I have for the whole usb_read_write.py is that I feel like
> instead of optimizing size of the "test file" we should generate the data on-
> the-fly and write it to two pipes, one to compute the hash for reference, and
> one that will be written to the drive.
>
> Or at least generate the random file in a less memory-hungry way.
> The reference md5 computation doesn't have to be done on a written file. First
> thought is to use hashlib from python, block by block. The real solution,
> however, is to use precomputed hashes. For any given size the hash can be
> known statically (there is a hardcoded seed).
>
> To sum up: content of RandomData could be written to the target file on the
> fly without a need of any huge buffer. If the size is known upfront
> precomputed hash can be used.
>
> I feel bad about circumventing the real problem in the script instead of
> fixing it.

Ha, the first line with +1 got changed to +0 when I kept reading the usb_read_write.py :)

Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote :

Freeing memory turned out to be quite challenging. I'd prefer to go with this working (but not perfect) version) til we can revisit the storage tests.

Revision history for this message
Maciej Kisielewski (kissiel) wrote :

Ack.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/checkbox_support/scripts/run_watcher.py b/checkbox_support/scripts/run_watcher.py
2index 50f2db1..9283db6 100644
3--- a/checkbox_support/scripts/run_watcher.py
4+++ b/checkbox_support/scripts/run_watcher.py
5@@ -228,4 +228,12 @@ def main():
6 watcher = LogWatcher("/var/log", callback, logfile="syslog")
7 signal.signal(signal.SIGALRM, no_usb_timeout)
8 signal.alarm(USB_INSERT_TIMEOUT)
9+ if ARGS.testcase == "insertion":
10+ print("\n\nINSERT NOW\n\n", flush=True)
11+ elif ARGS.testcase == "removal":
12+ print("\n\nREMOVE NOW\n\n", flush=True)
13 watcher.loop()
14+
15+
16+if __name__ == "__main__":
17+ main()
18diff --git a/checkbox_support/scripts/usb_read_write.py b/checkbox_support/scripts/usb_read_write.py
19index c589def..9dc63ff 100644
20--- a/checkbox_support/scripts/usb_read_write.py
21+++ b/checkbox_support/scripts/usb_read_write.py
22@@ -36,12 +36,20 @@ PLAINBOX_SESSION_SHARE = os.environ.get('PLAINBOX_SESSION_SHARE', '')
23 FOLDER_TO_MOUNT = tempfile.mkdtemp()
24 REPETITION_NUM = 5 # number to repeat the read/write test units.
25 # Prepare a random file which size is RANDOM_FILE_SIZE.
26-# Default 1048576 = 1MB to perform writing test
27-RANDOM_FILE_SIZE = 104857600
28+RANDOM_FILE_SIZE = 104857600 # 100 MiB
29+mem_bytes = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES')
30+mem_mib = mem_bytes/(1024.**2)
31+# On systems with less than 1 GiB of RAM, only generate a 20 MiB file
32+if mem_mib < 1200:
33+ RANDOM_FILE_SIZE = 20971520
34 USB_INSERT_INFO = "usb_insert_info"
35
36 log_path = os.path.join(PLAINBOX_SESSION_SHARE, 'usb-rw.log')
37 logging.basicConfig(level=logging.DEBUG, filename=log_path)
38+ch = logging.StreamHandler(sys.stdout)
39+ch.setFormatter(logging.Formatter("%(levelname)s:%(message)s"))
40+log = logging.getLogger('')
41+log.addHandler(ch)
42
43
44 class RandomData():
45@@ -88,6 +96,7 @@ class RandomData():
46 data = self._generate_test_data()
47 while os.path.getsize(self.tfile.name) < size:
48 self.tfile.write(next(data).encode('UTF-8'))
49+ self.tfile.close()
50 return self
51
52
53@@ -120,7 +129,6 @@ def get_partition_info():
54 else:
55 logging.error("has no idea which partition to mount or not found")
56 sys.exit(1)
57-
58 return partition
59
60
61@@ -160,7 +168,6 @@ def mount_usb_storage(partition):
62 # use pipe so I could hide message like
63 # "umount: /tmp/tmpjzwb6lys: not mounted"
64 subprocess.call(['umount', FOLDER_TO_MOUNT], stderr=subprocess.PIPE)
65-
66 # mount the target device/partition
67 # if the return code of the shell command is non-zero,
68 # means something wrong.
69@@ -173,7 +180,6 @@ def mount_usb_storage(partition):
70 logging.debug("mount %s on %s successfully."
71 % (device_to_mount, FOLDER_TO_MOUNT))
72 yield
73-
74 finally:
75 logging.info("context manager exit: unmount USB storage")
76 if subprocess.call(['umount', FOLDER_TO_MOUNT]):
77@@ -187,7 +193,6 @@ def read_test(random_file):
78 logging.debug("===================")
79 logging.debug("reading test begins")
80 logging.debug("===================")
81-
82 read_test_list = []
83 for idx in range(REPETITION_NUM):
84 read_test_list.append(read_test_unit(random_file, str(idx)))
85@@ -213,14 +218,11 @@ def read_test_unit(random_source_file, idx=""):
86 process = subprocess.Popen(['md5sum', random_source_file.tfile.name],
87 stdout=subprocess.PIPE)
88 source_md5sum = process.communicate()[0].decode().split(" ")[0]
89-
90 logging.debug("%s %s (verified)" % (tfile_md5sum, path_random_file))
91 logging.debug("%s %s (source)"
92 % (source_md5sum, random_source_file.tfile.name))
93-
94 # Clean the target file
95 os.remove(path_random_file)
96-
97 # verify the md5sum
98 if tfile_md5sum == source_md5sum:
99 print("PASS: READING TEST: %s passes md5sum comparison."
100@@ -238,7 +240,6 @@ def write_test(random_file):
101 logging.debug("===================")
102 logging.debug("writing test begins")
103 logging.debug("===================")
104-
105 write_speed_list = []
106 for idx in range(REPETITION_NUM):
107 write_speed_list.append(write_test_unit(random_file, str(idx)))
108@@ -253,13 +254,14 @@ def write_test_unit(random_file, idx=""):
109 """
110 perform the writing test.
111
112- :param random_file: a RndomData object created to be written
113+ :param random_file: a RandomData object created to be written
114 :return: a float in MB/s to denote writing speed
115 """
116 target_file = os.path.join(
117 FOLDER_TO_MOUNT, os.path.basename(random_file.tfile.name)) + idx
118 process = subprocess.Popen([
119- 'dd', 'if=' + random_file.tfile.name, 'of=' + target_file],
120+ 'dd', 'if=' + random_file.tfile.name, 'of=' + target_file, 'bs=1M',
121+ 'oflag=sync'],
122 stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
123 logging.debug("Apply command: %s" % process.args)
124 # will get something like
125@@ -267,8 +269,6 @@ def write_test_unit(random_file, idx=""):
126 # '1049076 bytes (1.0 MB) copied, 0.00473357 s, 222 MB/s', '']
127 list_dd_message = process.communicate()[0].decode().split("\n")
128 logging.debug(list_dd_message)
129- logging.debug(get_md5sum(target_file))
130-
131 try:
132 dd_speed = float(list_dd_message[2].split(" ")[-2])
133 print("PASS: WRITING TEST: %s" % target_file)
134@@ -279,7 +279,6 @@ def write_test_unit(random_file, idx=""):
135 # (20 MB) copied, 99.647 s, 200 kB/s', '']
136 print("ERROR: {}".format(list_dd_message))
137 sys.exit(1)
138-
139 return dd_speed
140
141
142@@ -291,18 +290,9 @@ def gen_random_file():
143 :return: a RandomData object
144 """
145 logging.debug("generating a random file")
146-
147 try:
148- # 1048576 = 1024 * 1024
149- # we are going to generate a 1M file
150 random_file = RandomData(RANDOM_FILE_SIZE)
151- # flush the remaining data in the memory buffer
152- # otherwise the md5sum will be different if you
153- # check it manually from your shell command md5sum
154- random_file.tfile.file.flush()
155-
156 yield random_file
157-
158 finally:
159 logging.info("Remove temporary folders and files.")
160 # delete the mount folder
161@@ -330,16 +320,14 @@ def get_md5sum(file_to_check):
162 # (b'07bc8f96b7c7dba2c1f3eb2f7dd50541 /tmp/tmp9jnuv329\n', None)
163 # will be returned by communicate() in this case
164 md5sum = process.communicate()[0].decode().split(" ")[0]
165-
166 if md5sum:
167- logging.debug("MD5SUM: of %s \t\t\t\t\t%s"
168+ logging.debug("MD5SUM of %s: %s"
169 % (file_to_check, md5sum))
170 return md5sum
171 else:
172 logging.error("Could not found file to check its MD5SUM. \
173 Check the folder permission?")
174 sys.exit(1)
175-
176 except OSError as e:
177 if e.errno == errno.ENOENT:
178 logging.error("%s info file was not found. \

Subscribers

People subscribed via source and target branches