diff options
-rw-r--r-- | tests/core/test_simd4.in | 39 | ||||
-rw-r--r-- | tests/core/test_simd4.out | 1 | ||||
-rw-r--r-- | tests/test_core.py | 9 | ||||
-rwxr-xr-x | tools/ffdb.py | 167 |
4 files changed, 188 insertions, 28 deletions
diff --git a/tests/core/test_simd4.in b/tests/core/test_simd4.in new file mode 100644 index 00000000..b597d8a3 --- /dev/null +++ b/tests/core/test_simd4.in @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <xmmintrin.h> + +static __inline__ __m128 __attribute__((__always_inline__)) +_mm_load_ps(const float *__p) +{ + return *(__m128*)__p; +} + +float simdAverage(float *src, int len) { + __m128 sumx4 = _mm_setzero_ps(); + for (int i = 0; i < len; i += 4) { + __m128 v = _mm_load_ps(src); + sumx4 = _mm_add_ps(sumx4, v); + src += 4; + } + float sumx4_mem[4]; + float *sumx4_ptr = sumx4_mem; + _mm_store_ps(sumx4_ptr, sumx4); + return (sumx4_mem[0] + sumx4_mem[1] + + sumx4_mem[2] + sumx4_mem[3])/len; +} + +void initArray(float *src, int len) { + for (int i = 0; i < len; ++i) { + src[i] = 0.1 * i; + } +} + +int main() { + const int len = 100000; + float src[len]; + float result = 0.0; + + initArray(src, len); + + result = simdAverage(src, len); + printf("averagex4 result: %.1f\n", result); +}
\ No newline at end of file diff --git a/tests/core/test_simd4.out b/tests/core/test_simd4.out new file mode 100644 index 00000000..99449772 --- /dev/null +++ b/tests/core/test_simd4.out @@ -0,0 +1 @@ +averagex4 result: 4999.9
\ No newline at end of file diff --git a/tests/test_core.py b/tests/test_core.py index bcb03830..b9057f4e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -4892,6 +4892,15 @@ return malloc(size); self.do_run_from_file(src, output) + def test_simd4(self): + # test_simd4 is to test phi node handling of SIMD path + if Settings.ASM_JS: Settings.ASM_JS = 2 # does not validate + + test_path = path_from_root('tests', 'core', 'test_simd4') + src, output = (test_path + s for s in ('.in', '.out')) + + self.do_run_from_file(src, output) + def test_gcc_unmangler(self): if os.environ.get('EMCC_FAST_COMPILER') == '0': Settings.NAMED_GLOBALS = 1 # test coverage for this; fastcomp never names globals diff --git a/tools/ffdb.py b/tools/ffdb.py index c22fd9db..2171cb0e 100755 --- a/tools/ffdb.py +++ b/tools/ffdb.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess +import socket, json, sys, uuid, datetime, time, logging, cgi, zipfile, os, tempfile, atexit, subprocess, re, base64, struct, imghdr LOG_VERBOSE = False # Verbose printing enabled with --verbose HOST = 'localhost' # The remote host to connect to the B2G device @@ -18,7 +18,13 @@ def sizeof_fmt(num): return "%3.1f%s" % (num, 'TB') def zipdir(path, zipfilename): - zipf = zipfile.ZipFile(zipfilename, 'w') + try: + import zlib + zip_mode = zipfile.ZIP_DEFLATED + except: + zip_mode = zipfile.ZIP_STORED + + zipf = zipfile.ZipFile(zipfilename, 'w', zip_mode) files_to_compress = [] for root, dirs, files in os.walk(path): for file in files: @@ -29,9 +35,10 @@ def zipdir(path, zipfilename): (root, file) = tuple filename = os.path.join(root, file) filesize = os.path.getsize(filename) - print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + os.path.relpath(filename, path) + '" (' + sizeof_fmt(filesize) + ')...' + path_in_archive = os.path.relpath(filename, path) + print 'Compressing ' + str(n) + '/' + str(len(files_to_compress)) + ': "' + path_in_archive + '" (' + sizeof_fmt(filesize) + ')...' n += 1 - zipf.write(os.path.join(root, file)) + zipf.write(os.path.join(root, file), path_in_archive) zipf.close() print 'Done. ' @@ -46,16 +53,17 @@ def format_html(msg): # Prints a verbose log message to stdout channel. Only shown if run with --verbose. def logv(msg): if LOG_VERBOSE: - sys.stdout.write(format_html(msg)) + sys.stdout.write(format_html(msg) + '\n') sys.stdout.flush() # Reads data from the socket, and tries to parse what we have got so far as a JSON message. # The messages are of form "bytelength:{jsondict}", where bytelength tells how many bytes # there are in the data that comes after the colon. # Returns a JSON dictionary of the received message. -def read_b2g_response(): +def read_b2g_response(print_errors_to_console = True): global read_queue, b2g_socket read_queue += b2g_socket.recv(65536*2) + payload = '' while ':' in read_queue: semicolon = read_queue.index(':') payload_len = int(read_queue[:semicolon]) @@ -66,10 +74,13 @@ def read_b2g_response(): read_queue = read_queue[semicolon+1+payload_len:] logv('Read a message of size ' + str(payload_len) + 'b from socket.') payload = json.loads(payload) + # Log received errors immediately to console + if print_errors_to_console and 'error' in payload: + print >> sys.stderr, 'Received error "' + payload['error'] + '"! Reason: ' + payload['message'] return payload # Sends a command to the B2G device and waits for the response and returns it as a JSON dict. -def send_b2g_cmd(to, cmd, data = {}): +def send_b2g_cmd(to, cmd, data = {}, print_errors_to_console = True): global b2g_socket msg = { 'to': to, 'type': cmd} msg = dict(msg.items() + data.items()) @@ -78,7 +89,7 @@ def send_b2g_cmd(to, cmd, data = {}): msg = str(len(msg))+':'+msg logv('Sending cmd:' + cmd + ' to:' + to) b2g_socket.sendall(msg) - return read_b2g_response() + return read_b2g_response(print_errors_to_console) def escape_bytes(b): return str(b) @@ -102,7 +113,16 @@ def send_b2g_data_chunk(to, data_blob): i += 1 message = '{"to":"'+to+'","type":"chunk","chunk":"' + ''.join(byte_str) + '"}' message = str(len(message)) + ':' + message + logv('{"to":"'+to+'","type":"chunk","chunk":"<data>"}') + b2g_socket.sendall(message) + return read_b2g_response() + +def send_b2g_bulk_data(to, data_blob): + message = 'bulk ' + to + ' stream ' + str(len(data_blob)) + ':' + logv(message) b2g_socket.sendall(message) + b2g_socket.sendall(data_blob) + # It seems that B2G doesn't send any response JSON back after a bulk transfer is finished, so no read_b2g_response() here. # Queries the device for a list of all installed apps. def b2g_get_appslist(): @@ -131,7 +151,7 @@ def print_applist(applist, running_app_manifests, print_removable): return num_printed def main(): - global b2g_socket, webappsActorName + global b2g_socket, webappsActorName, HOST, PORT, VERBOSE if len(sys.argv) < 2 or '--help' in sys.argv or 'help' in sys.argv or '-v' in sys.argv: print '''Firefox OS Debug Bridge, a tool for automating FFOS device tasks from the command line. @@ -148,22 +168,57 @@ def main(): log <app> [--clear]: Starts a persistent log listener that reads web console messages from the given application. If --clear is passed, the message log for that application is cleared instead. navigate <url>: Opens the given web page in the B2G browser. + screenshot [filename.png]: Takes a screenshot of the current contents displayed on the device. If an optional + filename is specified, the screenshot is saved to that file. Otherwise the filename + will be autogenerated. + + Options: Additionally, the following options may be passed to control FFDB execution: + + --host <hostname>: Specifies the target network address to connect to. Default: 'localhost'. + --port <number>: Specifies the network port to connect to. Default: 6000. + --verbose: Enables verbose printing, mostly useful for debugging. + --simulator: Signal that we will be connecting to a FFOS simulator and not a real device. In the above, whenever a command requires an <app> to be specified, either the human-readable name, localId or manifestURL of the application can be used.''' sys.exit(0) + connect_to_simulator = False + + options_with_value = ['--host', '--port'] + options = options_with_value + ['--verbose', '--simulator'] + # Process options + for i in range(0, len(sys.argv)): + if sys.argv[i] in options_with_value: + if i+1 >= sys.argv or sys.argv[i+1].startswith('-'): + print >> sys.stderr, "Missing value for option " + sys.argv[i] +'!' + sys.exit(1) + if sys.argv[i] == '--host': + HOST = sys.argv[i+1] + elif sys.argv[i] == '--port': + PORT = int(sys.argv[i+1]) + elif sys.argv[i] == '--verbose': + VERBOSE = True + elif sys.argv[i] == '--simulator': + connect_to_simulator = True + + # Clear the processed options so that parsing the commands below won't trip up on these. + if sys.argv[i] in options: sys.argv[i] = '' + if sys.argv[i] in options_with_value: sys.argv[i+1] = '' + + sys.argv = filter(lambda x: len(x) > 0, sys.argv) + b2g_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: b2g_socket.connect((HOST, PORT)) except Exception, e: if e[0] == 61: # Connection refused - if HOST == 'localhost' or HOST == '127.0.0.1': + if (HOST == 'localhost' or HOST == '127.0.0.1') and not connect_to_simulator: cmd = ['adb', 'forward', 'tcp:'+str(PORT), 'localfilesystem:/data/local/debugger-socket'] print 'Connection to ' + HOST + ':' + str(PORT) + ' refused, attempting to forward device debugger-socket to local address by calling ' + str(cmd) + ':' else: - print 'Error! Failed to connect to B2G device debugger socket at address ' + HOST + ':' + str(PORT) + '!' + print 'Error! Failed to connect to B2G ' + ('simulator' if connect_to_simulator else 'device') + ' debugger socket at address ' + HOST + ':' + str(PORT) + '!' sys.exit(1) try: retcode = subprocess.check_call(cmd) @@ -241,28 +296,35 @@ def main(): print 'Uploading application package "' + target_app_path + '"...' print 'Size of compressed package: ' + sizeof_fmt(os.path.getsize(target_app_path)) + '.' - uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage') - packageUploadActor = uploadResponse['actor'] app_file = open(target_app_path, 'rb') data = app_file.read() file_size = len(data) - chunk_size = 4*1024*1024 - i = 0 + + uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage', { 'bulk': 'true'}, print_errors_to_console = False) # This may fail if on old device. start_time = time.time() - while i < file_size: - chunk = data[i:i+chunk_size] - - send_b2g_data_chunk(packageUploadActor, chunk) - i += chunk_size - bytes_uploaded = min(i, file_size) - cur_time = time.time() - secs_elapsed = cur_time - start_time - percentage_done = bytes_uploaded * 1.0 / file_size - total_time = secs_elapsed / percentage_done - time_left = total_time - secs_elapsed - print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed) - send_b2g_cmd(webappsActorName, 'install', { 'appId': str(uuid.uuid4()), 'upload': packageUploadActor }) + if 'actor' in uploadResponse and 'BulkActor' in uploadResponse['actor']: # New B2G 2.0 hotness: binary data transfer + packageUploadActor = uploadResponse['actor'] + send_b2g_bulk_data(packageUploadActor, data) + else: # Old B2G 1.4 and older, serialize binary data in JSON text strings (SLOW!) + print 'Bulk upload is not supported, uploading binary data with old slow format. Consider flashing your device to FFOS 2.0 or newer to enjoy faster upload speeds.' + uploadResponse = send_b2g_cmd(webappsActorName, 'uploadPackage') + packageUploadActor = uploadResponse['actor'] + chunk_size = 4*1024*1024 + i = 0 + while i < file_size: + chunk = data[i:i+chunk_size] + + send_b2g_data_chunk(packageUploadActor, chunk) + i += chunk_size + bytes_uploaded = min(i, file_size) + cur_time = time.time() + secs_elapsed = cur_time - start_time + percentage_done = bytes_uploaded * 1.0 / file_size + total_time = secs_elapsed / percentage_done + time_left = total_time - secs_elapsed + print sizeof_fmt(bytes_uploaded) + " uploaded, {:5.1f} % done.".format(percentage_done*100.0) + ' Elapsed: ' + str(int(secs_elapsed)) + ' seconds. Time left: ' + str(datetime.timedelta(seconds=int(time_left))) + '. Data rate: {:5.2f} KB/second.'.format(bytes_uploaded / 1024.0 / secs_elapsed) + send_b2g_cmd(webappsActorName, 'install', { 'appId': str(uuid.uuid4()), 'upload': packageUploadActor }) cur_time = time.time() secs_elapsed = cur_time - start_time print 'Upload of ' + sizeof_fmt(file_size) + ' finished. Total time elapsed: ' + str(int(secs_elapsed)) + ' seconds. Data rate: {:5.2f} KB/second.'.format(file_size / 1024.0 / secs_elapsed) @@ -330,6 +392,55 @@ def main(): log_b2g_message(msg) else: print 'Application "' + sys.argv[2] + '" is not running!' + elif sys.argv[1] == 'screenshot': + if len(sys.argv) >= 3: + filename = sys.argv[2] + if not filename.endswith('.png'): + print >> sys.stderr, "Writing screenshots only to .png files are supported!" + sys.exit(1) + else: + filename = time.strftime("screen_%Y%m%d_%H%M%S.png", time.gmtime()) + + data_reply = send_b2g_cmd(deviceActorName, 'screenshotToDataURL') + data = data_reply['value'] + data_get_actor = data['actor'] + data_len = int(data['length']) + data_str = data['initial'] + delim = re.search(",", data_str).start() + data_format = data_str[:delim] + if data_format != "data:image/png;base64": + print >> sys.stderr, "Error: Received screenshot from device in an unexpected format '" + data_format + "'!" + sys.exit(1) + data = data_str[delim+1:] + chunk_size = 65000 + pos = len(data_str) + while pos < data_len: + bytes_to_read = min(data_len - pos, chunk_size) + data_reply = send_b2g_cmd(data_get_actor, 'substring', { 'start': str(pos), 'end': str(pos + bytes_to_read) }) + if len(data_reply['substring']) != bytes_to_read: + print >> sys.stderr, 'Error! Expected to receive ' + str(bytes_to_read) + ' bytes of image data, but got ' + str(len(data_reply['substring'])) + ' bytes instead!' + sys.exit(1) + data += data_reply['substring'] + pos += bytes_to_read + send_b2g_cmd(data_get_actor, 'release') # We need to explicitly free the screenshot image string from the device, or the Devtools connection leaks resources! + binary_data = base64.b64decode(data) + open(filename, 'wb').write(binary_data) + + def get_png_image_size(filename): + fhandle = open(filename, 'rb') + head = fhandle.read(24) + if len(head) != 24: + return (-1, -1) + check = struct.unpack('>i', head[4:8])[0] + if check != 0x0d0a1a0a: + return (-1, -1) + return struct.unpack('>ii', head[16:24]) + + width, height = get_png_image_size(filename) + if width <= 0 or height <= 0: + print >> sys.stderr, "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "', but the contents may be corrupted!" + else: + print "Wrote " + sizeof_fmt(len(binary_data)) + " to file '" + filename + "' (" + str(width) + 'x' + str(height) + ' pixels).' else: print "Unknown command '" + sys.argv[1] + "'! Pass --help for instructions." |