summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornoelallen@chromium.org <noelallen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-02 23:34:13 +0000
committernoelallen@chromium.org <noelallen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-02 23:34:13 +0000
commit9664e6a240dd053eed85ff171eb0ca82c278290a (patch)
treecba639620ded69a548b24b51cce493c9e2e33834
parent6dc71404e5ba1644dcf70bb184981f0f184e4801 (diff)
downloadchromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.zip
chromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.tar.gz
chromium_src-9664e6a240dd053eed85ff171eb0ca82c278290a.tar.bz2
Fix debugging example.
This example is now more of a tutorial on logging and crash handling practicies. The example can now run within the packaged app. R=binji@chromium.org BUG= Review URL: https://codereview.chromium.org/14659005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198008 0039d316-1c4b-4281-b951-d872f2087c98
-rwxr-xr-xnative_client_sdk/src/build_tools/build_sdk.py2
-rw-r--r--native_client_sdk/src/build_tools/generate_make.py1
-rw-r--r--native_client_sdk/src/build_tools/template.mk3
-rw-r--r--native_client_sdk/src/examples/common.js18
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/example.dsc20
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/example.js107
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/handler.py124
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/hello_world.c22
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/index.html94
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c270
-rw-r--r--native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h27
-rw-r--r--native_client_sdk/src/libraries/error_handling/error_handling.c266
-rw-r--r--native_client_sdk/src/libraries/error_handling/error_handling.h85
-rw-r--r--native_client_sdk/src/libraries/error_handling/library.dsc24
-rw-r--r--native_client_sdk/src/libraries/error_handling/string_stream.c (renamed from native_client_sdk/src/examples/tutorial/debugging/string_stream.c)9
-rw-r--r--native_client_sdk/src/libraries/error_handling/string_stream.h (renamed from native_client_sdk/src/examples/tutorial/debugging/string_stream.h)8
-rw-r--r--native_client_sdk/src/tools/nacl_gcc.mk34
17 files changed, 609 insertions, 505 deletions
diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py
index 7d7cd48..2e7d4eb 100755
--- a/native_client_sdk/src/build_tools/build_sdk.py
+++ b/native_client_sdk/src/build_tools/build_sdk.py
@@ -251,6 +251,7 @@ HEADER_MAP = {
'nacl/dynamic_annotations.h':
'src/untrusted/valgrind/dynamic_annotations.h',
'nacl/nacl_dyncode.h': 'src/untrusted/nacl/nacl_dyncode.h',
+ 'nacl/nacl_exception.h': 'src/include/nacl/nacl_exception.h',
'nacl/nacl_startup.h': 'src/untrusted/nacl/nacl_startup.h',
'nacl/nacl_thread.h': 'src/untrusted/nacl/nacl_thread.h',
'pnacl.h': 'src/untrusted/nacl/pnacl.h',
@@ -261,6 +262,7 @@ HEADER_MAP = {
'nacl/dynamic_annotations.h':
'src/untrusted/valgrind/dynamic_annotations.h',
'nacl/nacl_dyncode.h': 'src/untrusted/nacl/nacl_dyncode.h',
+ 'nacl/nacl_exception.h': 'src/include/nacl/nacl_exception.h',
'nacl/nacl_startup.h': 'src/untrusted/nacl/nacl_startup.h',
'nacl/nacl_thread.h': 'src/untrusted/nacl/nacl_thread.h',
'pnacl.h': 'src/untrusted/nacl/pnacl.h',
diff --git a/native_client_sdk/src/build_tools/generate_make.py b/native_client_sdk/src/build_tools/generate_make.py
index d6b2b6c..802a348 100644
--- a/native_client_sdk/src/build_tools/generate_make.py
+++ b/native_client_sdk/src/build_tools/generate_make.py
@@ -182,6 +182,7 @@ def ProcessProject(srcroot, dstroot, desc, toolchains, configs=None,
template_dict = {
'rel_sdk': '/'.join(['..'] * (len(desc['DEST'].split('/')) + 1)),
'pre': desc.get('PRE', ''),
+ 'post': desc.get('POST', ''),
'tools': tools,
'targets': desc['TARGETS'],
}
diff --git a/native_client_sdk/src/build_tools/template.mk b/native_client_sdk/src/build_tools/template.mk
index 2ae0f6f..1b3e5fe 100644
--- a/native_client_sdk/src/build_tools/template.mk
+++ b/native_client_sdk/src/build_tools/template.mk
@@ -106,3 +106,6 @@ endif
# Specify the NMF to be created with no additional arguments.
#
$(eval $(call NMF_RULE,$(TARGET),))
+
+{{post}}
+
diff --git a/native_client_sdk/src/examples/common.js b/native_client_sdk/src/examples/common.js
index 319c1f2..79988ed 100644
--- a/native_client_sdk/src/examples/common.js
+++ b/native_client_sdk/src/examples/common.js
@@ -27,6 +27,7 @@ var common = (function () {
moduleEl.setAttribute('id', 'nacl_module');
moduleEl.setAttribute('width', width);
moduleEl.setAttribute('height',height);
+ moduleEl.setAttribute('path', path);
moduleEl.setAttribute('src', path + '/' + name + '.nmf');
// Add any optional arguments
@@ -80,16 +81,29 @@ var common = (function () {
var listenerDiv = document.getElementById('listener');
listenerDiv.addEventListener('load', moduleDidLoad, true);
listenerDiv.addEventListener('message', handleMessage, true);
-
+ listenerDiv.addEventListener('crash', handleCrash, true);
if (typeof window.attachListeners !== 'undefined') {
window.attachListeners();
}
}
+
+ /**
+ * Called when the Browser can not communicate with the Module
+ *
+ * This event listener is registered in attachDefaultListeners above.
+ */
+ function handleCrash(event) {
+ updateStatus('CRASHED')
+ if (typeof window.handleCrash !== 'undefined') {
+ window.handleCrash(common.naclModule.lastError);
+ }
+ }
+
/**
* Called when the NaCl module is loaded.
*
- * This event listener is registered in createNaClModule above.
+ * This event listener is registered in attachDefaultListeners above.
*/
function moduleDidLoad() {
common.naclModule = document.getElementById('nacl_module');
diff --git a/native_client_sdk/src/examples/tutorial/debugging/example.dsc b/native_client_sdk/src/examples/tutorial/debugging/example.dsc
index d7edfc6..72cd94b 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/example.dsc
+++ b/native_client_sdk/src/examples/tutorial/debugging/example.dsc
@@ -4,7 +4,6 @@
'SEARCH': [
'.',
'../..',
- '../../../tools',
],
'TARGETS': [
{
@@ -12,28 +11,21 @@
'TYPE' : 'main',
'SOURCES' : [
'hello_world.c',
- 'string_stream.c',
- 'string_stream.h',
- 'untrusted_crash_dump.c',
- 'untrusted_crash_dump.h'
],
'CCFLAGS': ['-fno-omit-frame-pointer'],
+ 'DEPS' : ['error_handling'],
'LIBS' : ['ppapi', 'pthread']
}
],
- # The debugging example needs to use a different HTTP server to handle POST
- # messages from the NaCl module.
- 'PRE': """
-CHROME_ARGS+=--no-sandbox
-CHROME_ENV:=NACL_DANGEROUS_ENABLE_FILE_ACCESS=1
-CHROME_ENV+=NACL_SECURITY_DISABLE=1
-CHROME_ENV+=NACL_UNTRUSTED_EXCEPTION_HANDLING=1
+ 'POST': """
+#
+# Specify the MAP files to be created.
+#
+$(eval $(call MAP_RULE,$(TARGET),$(TARGET)))
""",
-
'DATA': [
'example.js',
- 'handler.py'
],
'DEST': 'examples/tutorial',
'NAME': 'debugging',
diff --git a/native_client_sdk/src/examples/tutorial/debugging/example.js b/native_client_sdk/src/examples/tutorial/debugging/example.js
index 7fdc4ba..6f637f8 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/example.js
+++ b/native_client_sdk/src/examples/tutorial/debugging/example.js
@@ -10,38 +10,112 @@ function domContentLoaded(name, tc, config, width, height) {
common.attachDefaultListeners();
updateStatus('Page Loaded');
- heartBeat();
}
// Indicate success when the NaCl module has loaded.
function moduleDidLoad() {
updateStatus('LOADED');
- setTimeout(boom, 4000);
+ setTimeout(boom, 2000);
+}
+
+function findAddress(addr, map) {
+ if (map.length < 1) {
+ return 'MAP Unavailable';
+ }
+ if (addr < map[0].offs) {
+ return 'Invalid Address';
+ }
+
+ for (var i=1; i < map.length; i++) {
+ if (addr < map[i].offs) {
+ var offs = addr - map[i-1].offs;
+ var filename = map[i-1].file;
+
+ // Force filename to 50 chars
+ if (filename) {
+ if (filename.length > 50) {
+ filename = '...' + filename.substr(filename.length - 47);
+ }
+ } else {
+ filename = 'Unknown';
+ }
+ while (filename.length < 50) {
+ filename = ' ' + filename
+ }
+ return filename + ' ' + map[i-1].name + ' + 0x' + offs.toString(16);
+ }
+ }
+
+ var last = map.length - 1;
+ return filename + ' ' + map[last].name + ' + 0x' + offs.toString(16);
+}
+
+function buildTextMap(map) {
+ map = map.split('\n');
+ var orderedMap = [];
+ for (var i=0; i < map.length; i++) {
+ var line = map[i].replace('\t', ' ');
+ var vals = line.split(' ');
+ var obj = {
+ offs: parseInt(vals[0], 16),
+ name: vals[2],
+ file: vals[3]
+ }
+ if (vals[1] && vals[1].toUpperCase() == 'T') {
+ orderedMap.push(obj);
+ }
+ }
+ orderedMap.sort(function(a,b) { return a.offs - b.offs; });
+ return orderedMap;
+}
+
+function updateStack(traceinfo, map) {
+ map = buildTextMap(map);
+ var text = 'Stack Trace\n';
+ for (var i=0; i < traceinfo.frames.length; i++) {
+ var frame = traceinfo.frames[i];
+ var addr = findAddress(frame.prog_ctr, map)
+ text += '[' + i.toString(10) + '] ' + addr + '\n';
+ }
+ document.getElementById('trace').value = text;
+}
+
+function fetchMap(url, traceinfo) {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open('GET', url, true);
+ xmlhttp.onload = function() {
+ updateStack(traceinfo, this.responseText);
+ }
+ xmlhttp.traceinfo = traceinfo;
+ xmlhttp.send();
}
// Handle a message coming from the NaCl module.
function handleMessage(message_event) {
msg_type = message_event.data.substring(0, 4);
msg_data = message_event.data.substring(5, message_event.data.length);
- if (msg_type == 'POP:') {
- alert(message_event.data);
- return;
- }
if (msg_type == 'LOG:') {
document.getElementById('log').value += msg_data + '\n';
return;
}
+ if (msg_type == 'STS:') {
+ updateStatus(msg_data);
+ }
if (msg_type == 'TRC:') {
crashed = true;
+ document.getElementById('json').value = msg_data;
+ crash_info = JSON.parse(msg_data);
updateStatus('Crash Reported');
- xmlhttp = new XMLHttpRequest();
- xmlhttp.open('POST', common.naclModule.src, false);
- xmlhttp.send(msg_data);
- document.getElementById('trace').value = xmlhttp.responseText + '\n';
+ src = common.naclModule.getAttribute('path');
+ fetchMap(src + '/debugging_' + crash_info['arch'] + '.map', crash_info);
return;
}
}
+function handleCrash(message) {
+ updateStatus(message);
+}
+
function updateStatus(message) {
common.updateStatus(message);
@@ -53,18 +127,5 @@ function boom() {
if (!crashed) {
updateStatus('Send BOOM');
common.naclModule.postMessage('BOOM');
- setTimeout(boom, 1000);
- }
-}
-
-function heartBeat() {
- if (common.naclModule && common.naclModule.lastError) {
- if (lastModuleError != common.naclModule.lastError) {
- lastModuleError = common.naclModule.lastError;
- updateStatus('Missed heartbeat: ' + common.naclModule.lastError);
- crashed = true;
- }
- } else {
- setTimeout(heartBeat, 1000);
}
}
diff --git a/native_client_sdk/src/examples/tutorial/debugging/handler.py b/native_client_sdk/src/examples/tutorial/debugging/handler.py
deleted file mode 100644
index 4593223..0000000
--- a/native_client_sdk/src/examples/tutorial/debugging/handler.py
+++ /dev/null
@@ -1,124 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Wrap the standard run.py to handle additional POST messages needed for
-debugging.
-
-See <NACL_SDK_ROOT>/tools/run.py for more information.
-"""
-
-import os
-import SimpleHTTPServer # pylint: disable=W0611
-import sys
-import urlparse
-
-SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
-NACL_SDK_ROOT = os.path.dirname(os.path.dirname(SCRIPT_DIR))
-TOOLS_DIR = os.path.join(NACL_SDK_ROOT, 'tools')
-
-sys.path.append(TOOLS_DIR)
-import decode_dump
-import getos
-
-
-last_nexe = None
-last_nmf = None
-
-
-# "Safely" split a string at |sep| into a [key, value] pair. If |sep| does not
-# exist in |pair|, then the entire |pair| is the key and the value is set to an
-# empty string.
-def KeyValuePair(pair, sep='='):
- if sep in pair:
- return pair.split(sep)
- else:
- return [pair, '']
-
-
-# $(NACL_SDK_ROOT)/tools/run.py looks for a file named handler.py in any
-# directory serving a file, then uses the class name HTTPRequestHandlerDelegate
-# to handle GET and POST requests for that directory.
-class HTTPRequestHandlerDelegate(object):
- def send_head(self, handler):
- """Common code for GET and HEAD commands.
-
- This sends the response code and MIME headers.
-
- Return value is either a file object (which has to be copied
- to the outputfile by the caller unless the command was HEAD,
- and must be closed by the caller under all circumstances), or
- None, in which case the caller has nothing further to do.
-
- """
- path = handler.translate_path(handler.path)
- f = None
- if os.path.isdir(path):
- if not handler.path.endswith('/'):
- # redirect browser - doing basically what apache does
- handler.send_response(301)
- handler.send_header("Location", handler.path + "/")
- handler.end_headers()
- return None
- for index in "index.html", "index.htm":
- index = os.path.join(path, index)
- if os.path.exists(index):
- path = index
- break
- else:
- return handler.list_directory(path)
- ctype = handler.guess_type(path)
- try:
- # Always read in binary mode. Opening files in text mode may cause
- # newline translations, making the actual size of the content
- # transmitted *less* than the content-length!
- f = open(path, 'rb')
- except IOError:
- handler.send_error(404, "File not found")
- return None
- handler.send_response(200)
- handler.send_header("Content-type", ctype)
- fs = os.fstat(f.fileno())
- handler.send_header("Content-Length", str(fs[6]))
- handler.send_header("Last-Modified", handler.date_time_string(fs.st_mtime))
- handler.send_header('Cache-Control','no-cache, must-revalidate')
- handler.send_header('Expires','-1')
- handler.end_headers()
- return f
-
- def do_GET(self, handler):
- global last_nexe, last_nmf
- (_, _, path, query, _) = urlparse.urlsplit(handler.path)
- url_params = dict([KeyValuePair(key_value)
- for key_value in query.split('&')])
- if 'quit' in url_params and '1' in url_params['quit']:
- handler.send_response(200, 'OK')
- handler.send_header('Content-type', 'text/html')
- handler.send_header('Content-length', '0')
- handler.end_headers()
- handler.server.shutdown()
- return
-
- if path.endswith('.nexe'):
- last_nexe = path
- if path.endswith('.nmf'):
- last_nmf = path
-
- handler.base_do_GET()
-
- def do_POST(self, handler):
- if 'Content-Length' in handler.headers:
- if not NACL_SDK_ROOT:
- handler.wfile('Could not find NACL_SDK_ROOT to decode trace.')
- return
- data = handler.rfile.read(int(handler.headers['Content-Length']))
- nexe = '.' + last_nexe
- nmf = '.' + last_nmf
- addr = os.path.join(NACL_SDK_ROOT, 'toolchain',
- getos.GetPlatform() + '_x86_newlib',
- 'bin', 'x86_64-nacl-addr2line')
- decoder = decode_dump.CoreDecoder(nexe, nmf, addr, None, None)
- info = decoder.Decode(data)
- trace = decoder.StackTrace(info)
- decoder.PrintTrace(trace, sys.stdout)
- decoder.PrintTrace(trace, handler.wfile)
diff --git a/native_client_sdk/src/examples/tutorial/debugging/hello_world.c b/native_client_sdk/src/examples/tutorial/debugging/hello_world.c
index a5dd333..8cb9fdb3 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/hello_world.c
+++ b/native_client_sdk/src/examples/tutorial/debugging/hello_world.c
@@ -25,8 +25,7 @@
#include <pthread.h>
-#include "string_stream.h"
-#include "untrusted_crash_dump.h"
+#include "error_handling/error_handling.h"
PPB_Messaging* ppb_messaging_interface = NULL;
PPB_Var* ppb_var_interface = NULL;
@@ -122,6 +121,14 @@ void PostMessage(const char *str) {
ppb_core_interface->CallOnMainThread(0, cb, 0);
}
+void DumpJson(const char* json) {
+ char* out = (char*) malloc(strlen(json) + 5);
+ strcpy(out, "TRC: ");
+ strcat(out, json);
+
+ PostMessage(out);
+ free(out);
+}
/**
* Called when the NaCl module is instantiated on the web page. The identifier
@@ -153,11 +160,16 @@ static PP_Bool Instance_DidCreate(PP_Instance instance,
g_PPAPIThread = pthread_self();
PostMessage("LOG: DidCreate");
- if (!NaClCrashDumpInit()) {
- PostMessage("LOG: Failed to set up crash dump.");
+
+ /* Request exception callbacks with JSON. */
+ EHRequestExceptionsJson(DumpJson);
+
+ /* Report back if the request was honored. */
+ if (!EHHanderInstalled()) {
+ PostMessage("LOG: Stack traces not available, so don't expect them.\n");
}
else {
- PostMessage("LOG: Crash Dump On");
+ PostMessage("LOG: Stack traces are on.");
}
pthread_create(&g_NexeThread, NULL, NexeMain, NULL);
return PP_TRUE;
diff --git a/native_client_sdk/src/examples/tutorial/debugging/index.html b/native_client_sdk/src/examples/tutorial/debugging/index.html
index d5d518b..da90b01 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/index.html
+++ b/native_client_sdk/src/examples/tutorial/debugging/index.html
@@ -8,49 +8,73 @@
<head>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
- <title>Logging and Stack Trace</title>
+ <title>Logging and Crash Handling</title>
<script type="text/javascript" src="common.js"></script>
<script type="text/javascript" src="example.js"></script>
</head>
-<body data-width="100" data-height="100" data-custom-load="true" {{attrs}}>
- <h1>Native Client Debugging Example: Generating a Stack Trace.</h1>
- <h2>How the example works</h2>
- <p>This example shows how to trap an untrusted exception (such as a illegal
- memory reference in the NEXE). This debugging technique can only be used
- for development since it requires several command-line switches, and
- environment variables. The test works by loading the module and communicating
- with it through PostMessage. Messages from the module are sent to the Status
- line and/or the Log window in the page. Four seconds after the module is
- loaded, the JavaScript on this page sends a 'BOOM' message to the module that
- causes the module to dereference an illegal location in memory.</p>
-
- <p>If your setup is correct (you launched Chrome with the appropriate
- command-line arguments and environment variables), the Log window below
- should show that the crash dump facilities are turned on. When the crash data
- arrives from the module, the data is forwarded to the HTTP server, which
- drives a decoder and sends back a stack trace to the web page.</p>
-
- <p>If setup incorrectly, the NaCl module may or may not load. If the module
- loads, it sends a "LOADED" message to the log and crash after four seconds.
- Since the stack trace facilities are not enables, the script will determine
- that the module has crashed by detecting a missed heartbeat which the
- application would normally send.
+<body data-width="0" data-height="0" data-custom-load="true" {{attrs}}>
+ <h1>Logging and Crash Handling</h1>
+ <p> This example illustrates techniques for tracking the state of a NaCl
+ module via PostMessage and status of the module's lastError attribute.
+ Messages from the modules are in the form of
+ <li>"LOG: <data>" which adds the message to the log.</li>
+ <li>"STS: <data>" which updates the status string.</li>
+ <li>"TRC: <data>" which provides a JSON string defining an exception.</li>
+ <h2> Exception API </h2>
+ <p> As of Chrome 28, NativeClient exception handling is possible without
+ requiring special command-line flags. This feature is not always available
+ so developers should avoid requring it under normal operation. However it
+ can be a very useful tool for diagnosing crashes, especially in the field.
+ NativeClient provides a library called "error_handling" for registering
+ the exception handler, as well as unwinding the exception context.
+ <br><b>NOTE: The library requires '-fno-omit-frame-pointer' to facilitate
+ unwinding the stack.</b></p>
+ <h2> Trace Walkthrough </h2>
+ <p> First we request the exception handler interface, and use it to register
+ both a handler and an exception stack. We use a separate stack since we
+ do not know the state of stack for the thread handling the exception.
+ Next, we create a worker thread which will take the exception. It is
+ recommended that modules do as much work as possible off the main thread.
+ Failure to do so can block the browser, making the page unresponsive and/or
+ preventing communication with JavaScript. In addition blocking calls,
+ which can greatly simplify code, are only allowed off the main thread.</p>
+ <p> After two seconds, JavaScript sends a message to the module which will
+ cause it take an exception on the worker thread. The exception handler
+ unwinds the stack while creating a stringified JSON object containing the
+ stack frame information. Once unwound, or the buffer is exhausted, the
+ JSON object is sent to JavaScript for processing.
+ <p> The message handler in JavaScript takes the JSON object and uses the
+ arch key to load the appropriate MAP file using an XMLHttpRequest. It
+ then processes the MAP file and prints out a stack trace using the exception
+ data in the JSON object.</p>
+ <h2> Exception Handling in the Field </h2>
+ <p>
+ For real world applications, it's important to get the crash information
+ back the developer. In this case, the JSON object could be sent via
+ XMLHttpRequest. The JSON object can the be processed by the developers
+ QA team to manage bugs in the field. The handler.py script provided in
+ the example sources shows how the JSON object can be used with the tools
+ to provide a better stack trace. Simply cut and paste the JSON object
+ to a text file and run the handler.py script on it.
</p>
- <h2>Running the example</h2>
- In a terminal window, to automatically start Chrome with the correct
- environment variables and command-line switches:
- <ul>
- <li>Set the CHROME_PATH environment variable to the fully qualified path of
- your Chrome executable.</li>
- <li>From the debugging directory type: <b>make RUN</b></li>
- </ul>
-
<div id="listener"></div>
<hr>
<h2>Status: <code id="statusField">NO-STATUS</code></h2>
- <h2>Log</h2>
- <textarea id="log" rows="10" cols="130" readonly="readonly"></textarea>
+ <table>
+ <tr>
+ <td><h2>Log</h2></td>
+ <td><h2>JSON</h2></td>
+ </tr>
+ <tr>
+ <td>
+ <textarea id="log" rows="10" cols="60" readonly="readonly"></textarea>
+ </td>
+ <td>
+ <textarea id="json" rows="10" cols="60" readonly="readonly"></textarea>
+ </td>
+ </tr>
+ </table>
<br>
<h2>Stack Trace</h2>
<textarea id="trace" rows="10" cols="130" readonly="readonly"></textarea>
diff --git a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c b/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c
deleted file mode 100644
index 959ad9e..0000000
--- a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (c) 2012 The Native Client Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include <assert.h>
-#include <inttypes.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-
-#ifdef __GLIBC__
-#include <elf.h>
-#include <link.h>
-#endif /* __GLIBC__ */
-
-#include "untrusted_crash_dump.h"
-#include "string_stream.h"
-#include "irt.h"
-
-struct NaClExceptionContext {
- uint32_t prog_ctr;
- uint32_t stack_ptr;
- uint32_t frame_ptr;
- /*
- * Pad this up to an even multiple of 8 bytes so this struct will add
- * a predictable amount of space to the various ExceptionFrame structures
- * used on each platform. This allows us to verify stack layout with dead
- * reckoning, without access to the ExceptionFrame structure used to set up
- * the call stack.
- */
- uint32_t pad;
-};
-
-#define CRASH_PAGE_CHUNK (64 * 1024)
-#define CRASH_STACK_SIZE (CRASH_PAGE_CHUNK * 4)
-#define CRASH_STACK_GUARD_SIZE CRASH_PAGE_CHUNK
-#define CRASH_STACK_COMPLETE_SIZE (CRASH_STACK_GUARD_SIZE + CRASH_STACK_SIZE)
-
-
-static pthread_key_t g_CrashStackKey;
-static struct nacl_irt_dev_exception_handling g_ExceptionHandling;
-static int g_ExceptionHandlingEnabled = 0;
-
-
-#ifdef __GLIBC__
-
-struct ProgramTableData {
- sstream_t *core;
- int first;
-};
-
-static void WriteJsonString(const char *str, sstream_t *core) {
- char ch;
-
- ssprintf(core, "\"");
- for (;;) {
- ch = *str++;
- if (ch == '\0') {
- break;
- } else if (ch == '"') {
- ssprintf(core, "\\\"");
- } else if (ch == '\\') {
- ssprintf(core, "\\\\");
- } else if (ch < 32 || ch > 126) {
- ssprintf(core, "\\x%02x", (uint8_t)ch);
- } else {
- ssprintf(core, "%c", ch);
- }
- }
- ssprintf(core, "\"");
-}
-
-static int PrintSegmentsOne(
- struct dl_phdr_info *info, size_t size, void *data) {
- int i;
- struct ProgramTableData *ptd = (struct ProgramTableData*) data;
-
- if (ptd->first) {
- ptd->first = 0;
- } else {
- fprintf(ptd->core, ",\n");
- }
- fprintf(ptd->core, "{\n");
- fprintf(ptd->core, "\"dlpi_name\": ");
- WriteJsonString(info->dlpi_name, ptd->core);
- fprintf(ptd->core, ",\n");
- fprintf(ptd->core, "\"dlpi_addr\": %u,\n",
- (uintptr_t) info->dlpi_addr);
- fprintf(ptd->core, "\"dlpi_phdr\": [\n");
- for (i = 0; i < info->dlpi_phnum; i++) {
- /* Skip non-LOAD type segments. */
- if (info->dlpi_phdr[i].p_type != PT_LOAD) {
- continue;
- }
- if (i != 0) {
- fprintf(ptd->core, ",\n");
- }
- fprintf(ptd->core, "{\n");
- fprintf(ptd->core, "\"p_vaddr\": %u,\n",
- (uintptr_t) info->dlpi_phdr[i].p_vaddr);
- fprintf(ptd->core, "\"p_memsz\": %u\n",
- (uintptr_t) info->dlpi_phdr[i].p_memsz);
- fprintf(ptd->core, "}\n");
- }
- fprintf(ptd->core, "]\n");
- fprintf(ptd->core, "}\n");
- return 0;
-}
-
-static void PrintSegments(sstream_t *core) {
- struct ProgramTableData data;
- data.core = core;
- data.first = 1;
- dl_iterate_phdr(PrintSegmentsOne, &data);
-}
-
-#else /* __GLIBC__ */
-
-static void PrintSegments(sstream_t *core) {
-}
-
-#endif /* __GLIBC__ */
-
-
-void PostMessage(const char *str);
-
-
-uintptr_t SafeRead(uintptr_t a) {
- /* TODO(bradnelson): use exception handling to recover from reads. */
- return *(uintptr_t*)a;
-}
-
-static void StackWalk(sstream_t *core, struct NaClExceptionContext *context) {
- uintptr_t next;
- uintptr_t i;
- int first = 1;
- uintptr_t prog_ctr = context->prog_ctr;
- uintptr_t frame_ptr = context->frame_ptr;
- uintptr_t args_start;
-
- ssprintf(core, "\"frames\": [\n");
- for (;;) {
- next = SafeRead(frame_ptr);
- if (next <= frame_ptr || next == 0) {
- break;
- }
- if (first) {
- first = 0;
- } else {
- ssprintf(core, ",");
- }
- ssprintf(core, "{\n");
- ssprintf(core, "\"frame_ptr\": %u,\n", frame_ptr);
- ssprintf(core, "\"prog_ctr\": %u,\n", prog_ctr);
- ssprintf(core, "\"data\": [\n");
-#if defined(__x86_64__)
- args_start = frame_ptr + 8;
-#else
- args_start = frame_ptr + 16;
-#endif
- for (i = args_start; i < next && i - args_start < 100; i += 4) {
- if (i != args_start) {
- ssprintf(core, ",");
- }
- ssprintf(core, "%u\n", SafeRead(i));
- }
- ssprintf(core, "]\n");
- ssprintf(core, "}\n");
-
- if (next - frame_ptr > 10000) break;
-#if defined(__x86_64__)
- prog_ctr = SafeRead(frame_ptr + 8);
-#else
- prog_ctr = SafeRead(frame_ptr + 4);
-#endif
- frame_ptr = next;
- }
-
- ssprintf(core, "]\n");
-}
-
-void CrashHandler(struct NaClExceptionContext *context) {
- sstream_t ss;
- FILE* handle = fopen("naclcorejson", "wb");
-
- ssinit(&ss);
- ssprintf(&ss, "TRC: {\n");
-
- ssprintf(&ss, "\"segments\": [");
- PrintSegments(&ss);
- ssprintf(&ss, "],\n");
-
- ssprintf(&ss, "\"handler\": {\n");
- ssprintf(&ss, "\"prog_ctr\": %u,\n", context->prog_ctr);
- ssprintf(&ss, "\"stack_ptr\": %u,\n", context->stack_ptr);
- ssprintf(&ss, "\"frame_ptr\": %u\n", context->frame_ptr);
- ssprintf(&ss, "},\n");
-
- StackWalk(&ss, context);
-
- ssprintf(&ss, "}\n");
-
- if (handle != NULL) {
- fprintf(handle, "%s", &ss.data[5]);
- fclose(handle);
- }
-
- PostMessage(ss.data);
-
- while(1);
-}
-
-void NaClCrashDumpThreadDestructor(void *arg) {
- munmap(arg, CRASH_STACK_COMPLETE_SIZE);
-}
-
-int NaClCrashDumpInit(void) {
- int result;
-
- assert(g_ExceptionHandlingEnabled == 0);
- if (nacl_interface_query(NACL_IRT_DEV_EXCEPTION_HANDLING_v0_1,
- &g_ExceptionHandling,
- sizeof(g_ExceptionHandling)) == 0) {
- fprintf(stderr, "ERROR: failed nacl_interface_query\n");
- return 0;
- }
- result = pthread_key_create(&g_CrashStackKey, NaClCrashDumpThreadDestructor);
- assert(result == 0);
- if (g_ExceptionHandling.exception_handler(CrashHandler, NULL) != 0) {
- fprintf(stderr, "ERROR: failed exception_handler\n");
- return 0;
- }
- g_ExceptionHandlingEnabled = 1;
- if (!NaClCrashDumpInitThread()) {
- g_ExceptionHandlingEnabled = 0;
- fprintf(stderr, "ERROR: failed InitThread\n");
- return 0;
- }
- return 1;
-}
-
-int NaClCrashDumpInitThread(void) {
- void *stack;
- void *guard;
- int result;
-
- if (!g_ExceptionHandlingEnabled) {
- return 0;
- }
- /*
- * NOTE: Setting up a per thread stack is only particularly interesting
- * for stack overflow.
- */
- stack = mmap(NULL, CRASH_STACK_COMPLETE_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- assert(stack != MAP_FAILED);
- guard = mmap(stack, CRASH_STACK_GUARD_SIZE,
- PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- assert(guard == stack);
- pthread_setspecific(g_CrashStackKey, stack);
- result = g_ExceptionHandling.exception_stack(
- stack, CRASH_STACK_COMPLETE_SIZE);
- return result == 0;
-}
diff --git a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h b/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h
deleted file mode 100644
index 8cd973a..0000000
--- a/native_client_sdk/src/examples/tutorial/debugging/untrusted_crash_dump.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2012 The Native Client Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/*
- * Untrusted crash dumper.
- */
-
-#ifndef NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__
-#define NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__ 1
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* These return non-zero on success. */
-int NaClCrashDumpInit(void);
-int NaClCrashDumpInitThread(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* NATIVE_CLIENT_SRC_UNTRUSTED_CRASH_DUMP_UNTRUSTED_CRASH_DUMP_H__ */
diff --git a/native_client_sdk/src/libraries/error_handling/error_handling.c b/native_client_sdk/src/libraries/error_handling/error_handling.c
new file mode 100644
index 0000000..e2fec20
--- /dev/null
+++ b/native_client_sdk/src/libraries/error_handling/error_handling.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#include <assert.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#ifdef __GLIBC__
+#include <elf.h>
+#include <link.h>
+#endif /* __GLIBC__ */
+
+#include "irt.h"
+#include "nacl/nacl_exception.h"
+
+#include "error_handling/error_handling.h"
+#include "error_handling/string_stream.h"
+
+
+#define PAGE_CHUNK_SIZE (64 * 1024)
+#define PAGE_CHUNK_MASK (PAGE_CHUNK_SIZE - 1)
+#define STACK_SIZE_MIN (PAGE_CHUNK_SIZE * 4)
+
+#define MAX_FRAME_SIZE (10 * 1024)
+#define MAX_FRAME_CAP 128
+
+static pthread_key_t s_eh_stack_info_key;
+static EHJsonHandler s_eh_json_callback = NULL;
+static int s_eh_exception_enabled = 0;
+static struct nacl_irt_exception_handling s_exception_handling;
+
+
+typedef struct {
+ void* stack;
+ size_t size;
+} EHStackInfo;
+
+static uintptr_t EHReadPointer(uintptr_t offset) {
+ return *((uintptr_t*) offset);
+}
+
+static void EHPrintArch(sstream_t* ss, struct NaClExceptionContext* context) {
+#if defined(__x86_64__)
+ ssprintf(ss, "\"arch\": \"x86_64\",\n");
+#elif defined(__i386__)
+ ssprintf(ss, "\"arch\": \"x86_32\",\n");
+#elif defined(__arm__)
+ ssprintf(ss, "\"arch\": \"arm\",\n");
+#elif defined(__mips__)
+ ssprintf(ss, "\"arch\": \"mips\",\n");
+#else
+#error Unknown ARCH
+#endif
+}
+
+static void EHPrintSegments(sstream_t* ss,
+ struct NaClExceptionContext* context) {
+ ssprintf(ss, "\"segments\": [");
+ ssprintf(ss, "],\n");
+}
+
+static void EHPrintFrame(sstream_t* ss, EHFrame* frame) {
+ uintptr_t start;
+ uintptr_t i;
+
+ ssprintf(ss, "{\n");
+ ssprintf(ss, "\"frame_ptr\": %u,\n", frame->frame_ptr);
+ ssprintf(ss, "\"prog_ctr\": %u,\n", frame->prog_ctr);
+ ssprintf(ss, "\"data\": [\n");
+
+#if defined(__x86_64__)
+ start = frame->frame_ptr + 8;
+#else
+ start = frame->frame_ptr + 16;
+#endif
+ /* Capture the stack, no mare than 128 bytes to keep the size sane. */
+ for (i = start; i < frame->next_ptr && i - start < MAX_FRAME_CAP; i += 4) {
+ if (i != start) {
+ ssprintf(ss, ",");
+ }
+ ssprintf(ss, "%u\n", EHReadPointer(i));
+ }
+ ssprintf(ss, "]\n}\n");
+}
+
+
+static void EHPrintMainContext(sstream_t* ss,
+ struct NaClExceptionContext* context) {
+ struct NaClExceptionPortableContext* portable_context =
+ nacl_exception_context_get_portable(context);
+ ssprintf(ss, "\"handler\": {\n");
+ ssprintf(ss, "\"prog_ctr\": %u,\n", portable_context->prog_ctr);
+ ssprintf(ss, "\"stack_ptr\": %u,\n", portable_context->stack_ptr);
+ ssprintf(ss, "\"frame_ptr\": %u\n", portable_context->frame_ptr);
+ ssprintf(ss, "},\n");
+}
+
+
+int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context,
+ EHFrame* frame) {
+ struct NaClExceptionPortableContext* portable_context =
+ nacl_exception_context_get_portable(context);
+
+ frame->prog_ctr = portable_context->prog_ctr;
+ frame->frame_ptr = portable_context->frame_ptr;
+ frame->next_ptr = EHReadPointer(portable_context->frame_ptr);
+ return 1;
+}
+
+
+int EHUnwindFrame(EHFrame* frame) {
+ uintptr_t frame_ptr;
+ uintptr_t next;
+
+ // Verify the current frame
+ if (NULL == frame) return 0;
+
+ frame_ptr = frame->frame_ptr;
+ next = frame->next_ptr;
+
+ // Abort if done or unwind moves us in the wrong direction
+ if (next <= frame_ptr || next == 0) return 0;
+
+ // Abort if frame is > 10K
+ if (next - frame_ptr > MAX_FRAME_SIZE) return 0;
+
+ // Unwind the frame
+ frame->frame_ptr = next;
+ frame->next_ptr = EHReadPointer(frame->frame_ptr);
+#if defined(__x86_64__)
+ frame->prog_ctr = EHReadPointer(frame_ptr + 8);
+#else
+ frame->prog_ctr = EHReadPointer(frame_ptr + 4);
+#endif
+ return 1;
+}
+
+
+static void EHStackInfoDestructor(void *arg) {
+ EHStackInfo* info = (EHStackInfo*) arg;
+
+ if (info) {
+ munmap(info->stack, info->size);
+ }
+ free(info);
+}
+
+
+void EHDefaultJsonHandler(struct NaClExceptionContext* context) {
+ if (s_eh_json_callback) {
+ EHFrame frame;
+ sstream_t ss;
+ ssinit(&ss);
+
+ ssprintf(&ss, "{\n");
+ EHPrintArch(&ss, context);
+ EHPrintSegments(&ss, context);
+ EHPrintMainContext(&ss, context);
+
+ EHGetTopFrame(&ss, context, &frame);
+ int first = 1;
+
+ ssprintf(&ss, "\"frames\": [\n");
+ do {
+ if (!first) ssprintf(&ss, ",");
+ EHPrintFrame(&ss, &frame);
+ first = 0;
+ } while (EHUnwindFrame(&frame));
+
+ /* End frame LIST and context DICT */
+ ssprintf(&ss, "]\n}\n");
+ s_eh_json_callback(ss.data);
+
+ ssfree(&ss);
+ while(1) sleep(9999);
+ }
+}
+
+
+void EHRequestExceptionsRaw(EHRawHandler callback) {
+ size_t interface_size = sizeof(s_exception_handling);
+ if (s_eh_exception_enabled) {
+ fprintf(stderr, "ERROR: EHInit already initialized.\n");
+ return;
+ }
+ if (NULL == callback) {
+ fprintf(stderr, "ERROR: EHInit called with NULL callback.\n");
+ return;
+ }
+
+ /* Expect an exact match on the interface structure size. */
+ if (nacl_interface_query(NACL_IRT_EXCEPTION_HANDLING_v0_1,
+ &s_exception_handling,
+ interface_size) != interface_size) {
+ fprintf(stderr, "ERROR: EHInit failed nacl_interface_query\n");
+ return;
+ }
+
+ if (s_exception_handling.exception_handler(callback, NULL) != 0) {
+ fprintf(stderr, "ERROR: EHInit failed to install exception_handler\n");
+ return;
+ }
+
+ s_eh_exception_enabled = 1;
+
+ // Create a TLS key for storing per thread stack info
+ pthread_key_create(&s_eh_stack_info_key, EHStackInfoDestructor);
+}
+
+
+void *EHRequestExceptionStackOnThread(size_t stack_size) {
+ void* stack;
+ void* guard;
+ EHStackInfo* stack_info;
+
+ // Set the stack size
+ stack_size = (stack_size + PAGE_CHUNK_MASK) & PAGE_CHUNK_MASK;
+ if (stack_size < STACK_SIZE_MIN) stack_size = STACK_SIZE_MIN;
+
+ // Allocate stack + guard page
+ stack = mmap(NULL, stack_size + PAGE_CHUNK_SIZE,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (MAP_FAILED == stack) return MAP_FAILED;
+
+ // Remap to mprotect which may not be available
+ guard = mmap(stack, PAGE_CHUNK_SIZE,
+ PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (guard != stack) {
+ fprintf(stderr, "WARNING: Failed to add guard page for alt stack.\n");
+ }
+
+ if (!s_exception_handling.exception_stack(stack, stack_size)) {
+ fprintf(stderr, "ERROR: Failed to assign stack.\n");
+ munmap(stack, stack_size);
+ return MAP_FAILED;
+ }
+
+ // Allocate stack tracking information for this thread
+ stack_info = (EHStackInfo*) malloc(sizeof(EHStackInfo));
+ stack_info->stack = stack;
+ stack_info->size = stack_size + PAGE_CHUNK_SIZE;
+ pthread_setspecific(s_eh_stack_info_key, stack_info);
+ return stack;
+}
+
+
+void EHRequestExceptionsJson(EHJsonHandler callback) {
+ if (NULL == callback) return;
+
+ EHRequestExceptionsRaw(EHDefaultJsonHandler);
+ if (s_eh_exception_enabled) s_eh_json_callback = callback;
+}
+
+
+int EHHanderInstalled() {
+ return s_eh_exception_enabled;
+} \ No newline at end of file
diff --git a/native_client_sdk/src/libraries/error_handling/error_handling.h b/native_client_sdk/src/libraries/error_handling/error_handling.h
new file mode 100644
index 0000000..29b4aca
--- /dev/null
+++ b/native_client_sdk/src/libraries/error_handling/error_handling.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef ERROR_HANDLING_ERROR_HANDLING_H_
+#define ERROR_HANDLING_ERROR_HANDLING_H_
+
+#include "error_handling/string_stream.h"
+#include "utils/macros.h"
+
+EXTERN_C_BEGIN
+
+struct NaClExceptionContext;
+
+typedef void (*EHRawHandler)(struct NaClExceptionContext* context);
+typedef void (*EHJsonHandler)(const char* str);
+
+typedef struct {
+ uint32_t prog_ctr;
+ uint32_t frame_ptr;
+ uint32_t next_ptr;
+} EHFrame;
+
+
+/** Initialize error handling.
+ *
+ * Initializes the error handling to catch untrusted exceptions. The init
+ * functions will install an untrusted exception handler.
+ *
+ * The raw form will install the provided handler directly to the exception
+ * system.
+ *
+ * The json form will install a default handler which will walk the stack
+ * creating a valid JSON string which is passed to the provided handler.
+ *
+ * NOTE: Exception handling is enabled process wide.
+ * NOTE: Exception handling is not guaranteed to be available so it should
+ * not be considered an error if the request fails.
+ */
+void EHRequestExceptionsRaw(EHRawHandler callback);
+void EHRequestExceptionsJson(EHJsonHandler callback);
+
+
+/** Request an alternate signal handling stack for this thread.
+ *
+ * Specifies an alternate stack which will be used when this thread enters
+ * the exception handler. This is useful in cases when the threads original
+ * stack may have overflowed or may be too small to handler the exception.
+ *
+ * Returns the allocated stack or MAP_FAILED.
+ *
+ * NOTE: Unlike the handler, this is a per thread call.
+ * NOTE: If the allocation fails, the exception will still take place on the
+ * thread's original stack.
+ */
+void *EHRequestExceptionStackOnThread(size_t stack_size);
+
+
+/** Determine if NaCl will forward exceptions.
+ *
+ * Returns non-zero if a hander has been installed and exceptions will
+ * be forwarded.
+ *
+ * NOTE: Exception handling is not guarenteed to be available so it should
+ * not be considered an error if the request fails.
+ */
+int EHHanderInstalled();
+
+
+/** Fill an exception frame from an exception context. */
+int EHGetTopFrame(sstream_t* ss, struct NaClExceptionContext* context,
+ EHFrame* frame);
+
+
+/** Unwind the stack by one frame.
+ *
+ * Returns zero once it failes to unwind.
+ */
+int EHUnwindFrame(EHFrame* frame);
+
+EXTERN_C_END
+
+#endif // ERROR_HANDLING_ERROR_HANDLING_H_ \ No newline at end of file
diff --git a/native_client_sdk/src/libraries/error_handling/library.dsc b/native_client_sdk/src/libraries/error_handling/library.dsc
new file mode 100644
index 0000000..f8ff7b6
--- /dev/null
+++ b/native_client_sdk/src/libraries/error_handling/library.dsc
@@ -0,0 +1,24 @@
+{
+ 'TOOLS': ['newlib'],
+ 'TARGETS': [
+ {
+ 'NAME' : 'error_handling',
+ 'TYPE' : 'lib',
+ 'SOURCES' : [
+ "error_handling.c",
+ "string_stream.c"
+ ],
+ }
+ ],
+ 'HEADERS': [
+ {
+ 'FILES': [
+ "error_handling.h",
+ "string_stream.h",
+ ],
+ 'DEST': 'include/error_handling',
+ },
+ ],
+ 'DEST': 'src',
+ 'NAME': 'error_handling',
+}
diff --git a/native_client_sdk/src/examples/tutorial/debugging/string_stream.c b/native_client_sdk/src/libraries/error_handling/string_stream.c
index 08783cc..4c1f7ed 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/string_stream.c
+++ b/native_client_sdk/src/libraries/error_handling/string_stream.c
@@ -1,8 +1,14 @@
+/*
+ * Copyright (c) 2013 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#include "string_stream.h"
+#include "error_handling/string_stream.h"
void ssinit(sstream_t *stream) {
stream->data = NULL;
@@ -32,6 +38,7 @@ int ssvprintf(sstream_t *stream, const char *format, va_list args) {
stream->data = outstr;
vsprintf(&stream->data[stream->length], format, hold);
stream->length += len;
+
return len;
}
diff --git a/native_client_sdk/src/examples/tutorial/debugging/string_stream.h b/native_client_sdk/src/libraries/error_handling/string_stream.h
index 9bb67e0..3c68dd6 100644
--- a/native_client_sdk/src/examples/tutorial/debugging/string_stream.h
+++ b/native_client_sdk/src/libraries/error_handling/string_stream.h
@@ -1,11 +1,11 @@
/*
- * Copyright (c) 2012 The Chromium Authors. All rights reserved.
+ * Copyright (c) 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
-#ifndef STRING_STREAM_H
-#define STRING_STREAM_H
+#ifndef ERROR_HANDLING_STRING_STREAM_H
+#define ERROR_HANDLING_STRING_STREAM_H
/*
* Support for a stream stream in 'C', which is appended to via an sprintf-like
@@ -28,4 +28,4 @@ int ssvprintf(sstream_t *sstream, const char *format, va_list args);
int ssprintf(sstream_t *sstream, const char *format, ...);
-#endif
+#endif /* ERROR_HANDLING_STRING_STREAM_H */
diff --git a/native_client_sdk/src/tools/nacl_gcc.mk b/native_client_sdk/src/tools/nacl_gcc.mk
index bdbe33d..ea55765 100644
--- a/native_client_sdk/src/tools/nacl_gcc.mk
+++ b/native_client_sdk/src/tools/nacl_gcc.mk
@@ -29,18 +29,21 @@ X86_32_CXX?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++
X86_32_LINK?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-g++
X86_32_LIB?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-ar
X86_32_STRIP?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-strip
+X86_32_NM?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/i686-nacl-nm
X86_64_CC?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-gcc
X86_64_CXX?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++
X86_64_LINK?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-g++
X86_64_LIB?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-ar
X86_64_STRIP?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-strip
+X86_64_NM?=$(TC_PATH)/$(OSNAME)_x86_$(TOOLCHAIN)/bin/x86_64-nacl-nm
ARM_CC?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-gcc
ARM_CXX?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++
ARM_LINK?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-g++
ARM_LIB?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-ar
ARM_STRIP?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-strip
+ARM_NM?=$(TC_PATH)/$(OSNAME)_arm_$(TOOLCHAIN)/bin/arm-nacl-nm
# Architecture-specific flags
@@ -236,6 +239,37 @@ endef
#
+# Strip Macro for each arch (e.g., each arch supported by MAP_RULE).
+#
+# $1 = Target Name
+# $2 = Source Name
+#
+define MAP_ALL_RULE
+$(OUTDIR)/$(1)_x86_32.map : $(OUTDIR)/$(2)_x86_32.nexe
+ $(call LOG,MAP,$$@,$(X86_32_NM) -l $$^ > $$@)
+
+$(OUTDIR)/$(1)_x86_64.map : $(OUTDIR)/$(2)_x86_64.nexe
+ $(call LOG,MAP,$$@,$(X86_64_NM) -l $$^ > $$@)
+
+$(OUTDIR)/$(1)_arm.map : $(OUTDIR)/$(2)_arm.nexe
+ $(call LOG,MAP,$$@,$(ARM_NM) -l $$^ > $$@ )
+
+all: $(OUTDIR)/$(1)_x86_32.map $(OUTDIR)/$(1)_x86_64.map $(OUTDIR)/$(1)_arm.map
+endef
+
+
+#
+# Top-level MAP Generation Macro
+#
+# $1 = Target Basename
+# $2 = Source Basename
+#
+define MAP_RULE
+$(call MAP_ALL_RULE,$(1),$(2))
+endef
+
+
+#
# Determine which architectures to build for. The user can set NACL_ARCH or
# ARCHES in the environment to control this.
#