aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarco Bubke <marco.bubke@qt.io>2016-11-14 12:28:51 +0100
committerOrgad Shaneh <orgads@gmail.com>2017-07-25 14:24:11 +0000
commit4beaae0f79ebe820fa9fdfdfb8195e5aeac89369 (patch)
tree3148e2c5032cfd79bed47787d0df97e0d9206f86
parent131a796f9f0afe10e744f27071f426af26296458 (diff)
Introduce Breakpad crash handler
Google Breakpad (https://chromium.googlesource.com/breakpad/breakpad) is a widely used crash handler framework, e.g. by Mozilla and Chromium. It is providing a platform neutral solution to generate mini dumps, collect debug information and generate stack traces from those. Done-with: Orgad Shaneh <orgad.shaneh@audiocodes.com> Change-Id: I09382e7db0dc9e29b228e7b554fda7b6f5684349 Reviewed-by: Orgad Shaneh <orgads@gmail.com> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
-rw-r--r--.gitignore1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp3
-rw-r--r--src/app/app.pro5
-rw-r--r--src/app/main.cpp8
-rw-r--r--src/libs/qt-breakpad/poster/__init__.py32
-rw-r--r--src/libs/qt-breakpad/poster/encode.py433
-rw-r--r--src/libs/qt-breakpad/poster/streaminghttp.py216
-rw-r--r--src/libs/qt-breakpad/qtbreakpad.pri64
-rw-r--r--src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.cpp222
-rw-r--r--src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.h56
-rwxr-xr-xsrc/libs/qt-breakpad/qtbreakpadsymbols271
-rw-r--r--src/libs/qt-breakpad/qtbreakpadsymbols.bat1
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler.pri21
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/detaildialog.cpp55
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/detaildialog.h48
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/dumpsender.cpp178
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/dumpsender.h55
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/main.cpp73
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp155
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/mainwidget.h70
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/mainwidget.ui143
-rw-r--r--src/libs/qt-breakpad/qtcrashhandler/qtcrashhandler.pro6
-rw-r--r--src/libs/qt-breakpad/testapp/main.cpp43
-rw-r--r--src/libs/qt-breakpad/testapp/testapp.pro14
-rw-r--r--src/tools/qml2puppet/qml2puppet/qml2puppet.pro3
-rw-r--r--src/tools/qtcrashhandler/qtcrashhandler.pro7
-rw-r--r--src/tools/tools.pro3
28 files changed, 2169 insertions, 22 deletions
diff --git a/.gitignore b/.gitignore
index 3e0ccac715..335868cd0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,7 @@ wrapper.sh
/src/app/Info.plist
/src/plugins/**/*.json
/src/plugins/coreplugin/ide_version.h
+/src/libs/qt-breakpad/bin
app_version.h
phony.c
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
index 5a10394f18..39599385a8 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
@@ -13,11 +13,6 @@ include (../interfaces/interfaces.pri)
include (../types/types.pri)
include (../qmlprivategate/qmlprivategate.pri)
-QT_BREAKPAD_ROOT_PATH = $$(QT_BREAKPAD_ROOT_PATH)
-!isEmpty(QT_BREAKPAD_ROOT_PATH) {
- include($$QT_BREAKPAD_ROOT_PATH/qtbreakpad.pri)
-}
-
SOURCES += $$PWD/qml2puppetmain.cpp
RESOURCES += $$PWD/../qmlpuppet.qrc
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
index 0d29b554e6..22460eaf6b 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
@@ -111,7 +111,8 @@ int internalMain(QGuiApplication *application)
#ifdef ENABLE_QT_BREAKPAD
- QtSystemExceptionHandler systemExceptionHandler;
+ const QString libexecPath = QCoreApplication::applicationDirPath() + '/' + RELATIVE_LIBEXEC_PATH;
+ QtSystemExceptionHandler systemExceptionHandler(libexecPath);
#endif
new QmlDesigner::Qt5NodeInstanceClientProxy(application);
diff --git a/src/app/app.pro b/src/app/app.pro
index 1414e7d7fc..4d6030bfe7 100644
--- a/src/app/app.pro
+++ b/src/app/app.pro
@@ -12,13 +12,10 @@ HEADERS += ../tools/qtcreatorcrashhandler/crashhandlersetup.h
SOURCES += main.cpp ../tools/qtcreatorcrashhandler/crashhandlersetup.cpp
include(../rpath.pri)
+include(../libs/qt-breakpad/qtbreakpad.pri)
LIBS *= -l$$qtLibraryName(ExtensionSystem) -l$$qtLibraryName(Aggregation) -l$$qtLibraryName(Utils)
-QT_BREAKPAD_ROOT_PATH = $$(QT_BREAKPAD_ROOT_PATH)
-!isEmpty(QT_BREAKPAD_ROOT_PATH) {
- include($$QT_BREAKPAD_ROOT_PATH/qtbreakpad.pri)
-}
win32 {
# We need the version in two separate formats for the .rc file
# RC_VERSION=4,3,82,0 (quadruple)
diff --git a/src/app/main.cpp b/src/app/main.cpp
index c1ae3245fb..4888b8db58 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -329,13 +329,13 @@ int main(int argc, char **argv)
const int threadCount = QThreadPool::globalInstance()->maxThreadCount();
QThreadPool::globalInstance()->setMaxThreadCount(qMax(4, 2 * threadCount));
- // Display a backtrace once a serious signal is delivered (Linux only).
const QString libexecPath = QCoreApplication::applicationDirPath()
+ '/' + RELATIVE_LIBEXEC_PATH;
- CrashHandlerSetup setupCrashHandler(appNameC, CrashHandlerSetup::EnableRestart, libexecPath);
-
#ifdef ENABLE_QT_BREAKPAD
- QtSystemExceptionHandler systemExceptionHandler;
+ QtSystemExceptionHandler systemExceptionHandler(libexecPath);
+#else
+ // Display a backtrace once a serious signal is delivered (Linux only).
+ CrashHandlerSetup setupCrashHandler(appNameC, CrashHandlerSetup::EnableRestart, libexecPath);
#endif
app.setAttribute(Qt::AA_UseHighDpiPixmaps);
diff --git a/src/libs/qt-breakpad/poster/__init__.py b/src/libs/qt-breakpad/poster/__init__.py
new file mode 100644
index 0000000000..e963d70b96
--- /dev/null
+++ b/src/libs/qt-breakpad/poster/__init__.py
@@ -0,0 +1,32 @@
+# Copyright (c) 2010 Chris AtLee
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+"""poster module
+
+Support for streaming HTTP uploads, and multipart/form-data encoding
+
+```poster.version``` is a 3-tuple of integers representing the version number.
+New releases of poster will always have a version number that compares greater
+than an older version of poster.
+New in version 0.6."""
+
+import poster.streaminghttp
+import poster.encode
+
+version = (0, 8, 0) # Thanks JP!
diff --git a/src/libs/qt-breakpad/poster/encode.py b/src/libs/qt-breakpad/poster/encode.py
new file mode 100644
index 0000000000..fbc4e76452
--- /dev/null
+++ b/src/libs/qt-breakpad/poster/encode.py
@@ -0,0 +1,433 @@
+# Copyright (c) 2010 Chris AtLee
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+"""multipart/form-data encoding module
+
+This module provides functions that faciliate encoding name/value pairs
+as multipart/form-data suitable for a HTTP POST or PUT request.
+
+multipart/form-data is the standard way to upload files over HTTP"""
+
+__all__ = ['gen_boundary', 'encode_and_quote', 'MultipartParam',
+ 'encode_string', 'encode_file_header', 'get_body_size', 'get_headers',
+ 'multipart_encode']
+
+try:
+ import uuid
+ def gen_boundary():
+ """Returns a random string to use as the boundary for a message"""
+ return uuid.uuid4().hex
+except ImportError:
+ import random, sha
+ def gen_boundary():
+ """Returns a random string to use as the boundary for a message"""
+ bits = random.getrandbits(160)
+ return sha.new(str(bits)).hexdigest()
+
+import urllib, re, os, mimetypes
+try:
+ from email.header import Header
+except ImportError:
+ # Python 2.4
+ from email.Header import Header
+
+def encode_and_quote(data):
+ """If ``data`` is unicode, return urllib.quote_plus(data.encode("utf-8"))
+ otherwise return urllib.quote_plus(data)"""
+ if data is None:
+ return None
+
+ if isinstance(data, unicode):
+ data = data.encode("utf-8")
+ return urllib.quote_plus(data)
+
+def _strify(s):
+ """If s is a unicode string, encode it to UTF-8 and return the results,
+ otherwise return str(s), or None if s is None"""
+ if s is None:
+ return None
+ if isinstance(s, unicode):
+ return s.encode("utf-8")
+ return str(s)
+
+class MultipartParam(object):
+ """Represents a single parameter in a multipart/form-data request
+
+ ``name`` is the name of this parameter.
+
+ If ``value`` is set, it must be a string or unicode object to use as the
+ data for this parameter.
+
+ If ``filename`` is set, it is what to say that this parameter's filename
+ is. Note that this does not have to be the actual filename any local file.
+
+ If ``filetype`` is set, it is used as the Content-Type for this parameter.
+ If unset it defaults to "text/plain; charset=utf8"
+
+ If ``filesize`` is set, it specifies the length of the file ``fileobj``
+
+ If ``fileobj`` is set, it must be a file-like object that supports
+ .read().
+
+ Both ``value`` and ``fileobj`` must not be set, doing so will
+ raise a ValueError assertion.
+
+ If ``fileobj`` is set, and ``filesize`` is not specified, then
+ the file's size will be determined first by stat'ing ``fileobj``'s
+ file descriptor, and if that fails, by seeking to the end of the file,
+ recording the current position as the size, and then by seeking back to the
+ beginning of the file.
+
+ ``cb`` is a callable which will be called from iter_encode with (self,
+ current, total), representing the current parameter, current amount
+ transferred, and the total size.
+ """
+ def __init__(self, name, value=None, filename=None, filetype=None,
+ filesize=None, fileobj=None, cb=None):
+ self.name = Header(name).encode()
+ self.value = _strify(value)
+ if filename is None:
+ self.filename = None
+ else:
+ if isinstance(filename, unicode):
+ # Encode with XML entities
+ self.filename = filename.encode("ascii", "xmlcharrefreplace")
+ else:
+ self.filename = str(filename)
+ self.filename = self.filename.encode("string_escape").\
+ replace('"', '\\"')
+ self.filetype = _strify(filetype)
+
+ self.filesize = filesize
+ self.fileobj = fileobj
+ self.cb = cb
+
+ if self.value is not None and self.fileobj is not None:
+ raise ValueError("Only one of value or fileobj may be specified")
+
+ if fileobj is not None and filesize is None:
+ # Try and determine the file size
+ try:
+ self.filesize = os.fstat(fileobj.fileno()).st_size
+ except (OSError, AttributeError):
+ try:
+ fileobj.seek(0, 2)
+ self.filesize = fileobj.tell()
+ fileobj.seek(0)
+ except:
+ raise ValueError("Could not determine filesize")
+
+ def __cmp__(self, other):
+ attrs = ['name', 'value', 'filename', 'filetype', 'filesize', 'fileobj']
+ myattrs = [getattr(self, a) for a in attrs]
+ oattrs = [getattr(other, a) for a in attrs]
+ return cmp(myattrs, oattrs)
+
+ def reset(self):
+ if self.fileobj is not None:
+ self.fileobj.seek(0)
+ elif self.value is None:
+ raise ValueError("Don't know how to reset this parameter")
+
+ @classmethod
+ def from_file(cls, paramname, filename):
+ """Returns a new MultipartParam object constructed from the local
+ file at ``filename``.
+
+ ``filesize`` is determined by os.path.getsize(``filename``)
+
+ ``filetype`` is determined by mimetypes.guess_type(``filename``)[0]
+
+ ``filename`` is set to os.path.basename(``filename``)
+ """
+
+ return cls(paramname, filename=os.path.basename(filename),
+ filetype=mimetypes.guess_type(filename)[0],
+ filesize=os.path.getsize(filename),
+ fileobj=open(filename, "rb"))
+
+ @classmethod
+ def from_params(cls, params):
+ """Returns a list of MultipartParam objects from a sequence of
+ name, value pairs, MultipartParam instances,
+ or from a mapping of names to values
+
+ The values may be strings or file objects, or MultipartParam objects.
+ MultipartParam object names must match the given names in the
+ name,value pairs or mapping, if applicable."""
+ if hasattr(params, 'items'):
+ params = params.items()
+
+ retval = []
+ for item in params:
+ if isinstance(item, cls):
+ retval.append(item)
+ continue
+ name, value = item
+ if isinstance(value, cls):
+ assert value.name == name
+ retval.append(value)
+ continue
+ if hasattr(value, 'read'):
+ # Looks like a file object
+ filename = getattr(value, 'name', None)
+ if filename is not None:
+ filetype = mimetypes.guess_type(filename)[0]
+ else:
+ filetype = None
+
+ retval.append(cls(name=name, filename=filename,
+ filetype=filetype, fileobj=value))
+ else:
+ retval.append(cls(name, value))
+ return retval
+
+ def encode_hdr(self, boundary):
+ """Returns the header of the encoding of this parameter"""
+ boundary = encode_and_quote(boundary)
+
+ headers = ["--%s" % boundary]
+
+ if self.filename:
+ disposition = 'form-data; name="%s"; filename="%s"' % (self.name,
+ self.filename)
+ else:
+ disposition = 'form-data; name="%s"' % self.name
+
+ headers.append("Content-Disposition: %s" % disposition)
+
+ if self.filetype:
+ filetype = self.filetype
+ else:
+ filetype = "text/plain; charset=utf-8"
+
+ headers.append("Content-Type: %s" % filetype)
+
+ headers.append("")
+ headers.append("")
+
+ return "\r\n".join(headers)
+
+ def encode(self, boundary):
+ """Returns the string encoding of this parameter"""
+ if self.value is None:
+ value = self.fileobj.read()
+ else:
+ value = self.value
+
+ if re.search("^--%s$" % re.escape(boundary), value, re.M):
+ raise ValueError("boundary found in encoded string")
+
+ return "%s%s\r\n" % (self.encode_hdr(boundary), value)
+
+ def iter_encode(self, boundary, blocksize=4096):
+ """Yields the encoding of this parameter
+ If self.fileobj is set, then blocks of ``blocksize`` bytes are read and
+ yielded."""
+ total = self.get_size(boundary)
+ current = 0
+ if self.value is not None:
+ block = self.encode(boundary)
+ current += len(block)
+ yield block
+ if self.cb:
+ self.cb(self, current, total)
+ else:
+ block = self.encode_hdr(boundary)
+ current += len(block)
+ yield block
+ if self.cb:
+ self.cb(self, current, total)
+ last_block = ""
+ encoded_boundary = "--%s" % encode_and_quote(boundary)
+ boundary_exp = re.compile("^%s$" % re.escape(encoded_boundary),
+ re.M)
+ while True:
+ block = self.fileobj.read(blocksize)
+ if not block:
+ current += 2
+ yield "\r\n"
+ if self.cb:
+ self.cb(self, current, total)
+ break
+ last_block += block
+ if boundary_exp.search(last_block):
+ raise ValueError("boundary found in file data")
+ last_block = last_block[-len(encoded_boundary)-2:]
+ current += len(block)
+ yield block
+ if self.cb:
+ self.cb(self, current, total)
+
+ def get_size(self, boundary):
+ """Returns the size in bytes that this param will be when encoded
+ with the given boundary."""
+ if self.filesize is not None:
+ valuesize = self.filesize
+ else:
+ valuesize = len(self.value)
+
+ return len(self.encode_hdr(boundary)) + 2 + valuesize
+
+def encode_string(boundary, name, value):
+ """Returns ``name`` and ``value`` encoded as a multipart/form-data
+ variable. ``boundary`` is the boundary string used throughout
+ a single request to separate variables."""
+
+ return MultipartParam(name, value).encode(boundary)
+
+def encode_file_header(boundary, paramname, filesize, filename=None,
+ filetype=None):
+ """Returns the leading data for a multipart/form-data field that contains
+ file data.
+
+ ``boundary`` is the boundary string used throughout a single request to
+ separate variables.
+
+ ``paramname`` is the name of the variable in this request.
+
+ ``filesize`` is the size of the file data.
+
+ ``filename`` if specified is the filename to give to this field. This
+ field is only useful to the server for determining the original filename.
+
+ ``filetype`` if specified is the MIME type of this file.
+
+ The actual file data should be sent after this header has been sent.
+ """
+
+ return MultipartParam(paramname, filesize=filesize, filename=filename,
+ filetype=filetype).encode_hdr(boundary)
+
+def get_body_size(params, boundary):
+ """Returns the number of bytes that the multipart/form-data encoding
+ of ``params`` will be."""
+ size = sum(p.get_size(boundary) for p in MultipartParam.from_params(params))
+ return size + len(boundary) + 6
+
+def get_headers(params, boundary):
+ """Returns a dictionary with Content-Type and Content-Length headers
+ for the multipart/form-data encoding of ``params``."""
+ headers = {}
+ boundary = urllib.quote_plus(boundary)
+ headers['Content-Type'] = "multipart/form-data; boundary=%s" % boundary
+ headers['Content-Length'] = str(get_body_size(params, boundary))
+ return headers
+
+class multipart_yielder:
+ def __init__(self, params, boundary, cb):
+ self.params = params
+ self.boundary = boundary
+ self.cb = cb
+
+ self.i = 0
+ self.p = None
+ self.param_iter = None
+ self.current = 0
+ self.total = get_body_size(params, boundary)
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ """generator function to yield multipart/form-data representation
+ of parameters"""
+ if self.param_iter is not None:
+ try:
+ block = self.param_iter.next()
+ self.current += len(block)
+ if self.cb:
+ self.cb(self.p, self.current, self.total)
+ return block
+ except StopIteration:
+ self.p = None
+ self.param_iter = None
+
+ if self.i is None:
+ raise StopIteration
+ elif self.i >= len(self.params):
+ self.param_iter = None
+ self.p = None
+ self.i = None
+ block = "--%s--\r\n" % self.boundary
+ self.current += len(block)
+ if self.cb:
+ self.cb(self.p, self.current, self.total)
+ return block
+
+ self.p = self.params[self.i]
+ self.param_iter = self.p.iter_encode(self.boundary)
+ self.i += 1
+ return self.next()
+
+ def reset(self):
+ self.i = 0
+ self.current = 0
+ for param in self.params:
+ param.reset()
+
+def multipart_encode(params, boundary=None, cb=None):
+ """Encode ``params`` as multipart/form-data.
+
+ ``params`` should be a sequence of (name, value) pairs or MultipartParam
+ objects, or a mapping of names to values.
+ Values are either strings parameter values, or file-like objects to use as
+ the parameter value. The file-like objects must support .read() and either
+ .fileno() or both .seek() and .tell().
+
+ If ``boundary`` is set, then it as used as the MIME boundary. Otherwise
+ a randomly generated boundary will be used. In either case, if the
+ boundary string appears in the parameter values a ValueError will be
+ raised.
+
+ If ``cb`` is set, it should be a callback which will get called as blocks
+ of data are encoded. It will be called with (param, current, total),
+ indicating the current parameter being encoded, the current amount encoded,
+ and the total amount to encode.
+
+ Returns a tuple of `datagen`, `headers`, where `datagen` is a
+ generator that will yield blocks of data that make up the encoded
+ parameters, and `headers` is a dictionary with the assoicated
+ Content-Type and Content-Length headers.
+
+ Examples:
+
+ >>> datagen, headers = multipart_encode( [("key", "value1"), ("key", "value2")] )
+ >>> s = "".join(datagen)
+ >>> assert "value2" in s and "value1" in s
+
+ >>> p = MultipartParam("key", "value2")
+ >>> datagen, headers = multipart_encode( [("key", "value1"), p] )
+ >>> s = "".join(datagen)
+ >>> assert "value2" in s and "value1" in s
+
+ >>> datagen, headers = multipart_encode( {"key": "value1"} )
+ >>> s = "".join(datagen)
+ >>> assert "value2" not in s and "value1" in s
+
+ """
+ if boundary is None:
+ boundary = gen_boundary()
+ else:
+ boundary = urllib.quote_plus(boundary)
+
+ headers = get_headers(params, boundary)
+ params = MultipartParam.from_params(params)
+
+ return multipart_yielder(params, boundary, cb), headers
diff --git a/src/libs/qt-breakpad/poster/streaminghttp.py b/src/libs/qt-breakpad/poster/streaminghttp.py
new file mode 100644
index 0000000000..eb3b4661d3
--- /dev/null
+++ b/src/libs/qt-breakpad/poster/streaminghttp.py
@@ -0,0 +1,216 @@
+# Copyright (c) 2010 Chris AtLee
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+"""Streaming HTTP uploads module.
+
+This module extends the standard httplib and urllib2 objects so that
+iterable objects can be used in the body of HTTP requests.
+
+In most cases all one should have to do is call :func:`register_openers()`
+to register the new streaming http handlers which will take priority over
+the default handlers, and then you can use iterable objects in the body
+of HTTP requests.
+
+**N.B.** You must specify a Content-Length header if using an iterable object
+since there is no way to determine in advance the total size that will be
+yielded, and there is no way to reset an interator.
+
+Example usage:
+
+>>> from StringIO import StringIO
+>>> import urllib2, poster.streaminghttp
+
+>>> opener = poster.streaminghttp.register_openers()
+
+>>> s = "Test file data"
+>>> f = StringIO(s)
+
+>>> req = urllib2.Request("http://localhost:5000", f,
+... {'Content-Length': str(len(s))})
+"""
+
+import httplib, urllib2, socket
+from httplib import NotConnected
+
+__all__ = ['StreamingHTTPConnection', 'StreamingHTTPRedirectHandler',
+ 'StreamingHTTPHandler', 'register_openers']
+
+if hasattr(httplib, 'HTTPS'):
+ __all__.extend(['StreamingHTTPSHandler', 'StreamingHTTPSConnection'])
+
+class _StreamingHTTPMixin:
+ """Mixin class for HTTP and HTTPS connections that implements a streaming
+ send method."""
+ def send(self, value):
+ """Send ``value`` to the server.
+
+ ``value`` can be a string object, a file-like object that supports
+ a .read() method, or an iterable object that supports a .next()
+ method.
+ """
+ # Based on python 2.6's httplib.HTTPConnection.send()
+ if self.sock is None:
+ if self.auto_open:
+ self.connect()
+ else:
+ raise NotConnected()
+
+ # send the data to the server. if we get a broken pipe, then close
+ # the socket. we want to reconnect when somebody tries to send again.
+ #
+ # NOTE: we DO propagate the error, though, because we cannot simply
+ # ignore the error... the caller will know if they can retry.
+ if self.debuglevel > 0:
+ print "send:", repr(value)
+ try:
+ blocksize = 8192
+ if hasattr(value, 'read') :
+ if hasattr(value, 'seek'):
+ value.seek(0)
+ if self.debuglevel > 0:
+ print "sendIng a read()able"
+ data = value.read(blocksize)
+ while data:
+ self.sock.sendall(data)
+ data = value.read(blocksize)
+ elif hasattr(value, 'next'):
+ if hasattr(value, 'reset'):
+ value.reset()
+ if self.debuglevel > 0:
+ print "sendIng an iterable"
+ for data in value:
+ self.sock.sendall(data)
+ else:
+ self.sock.sendall(value)
+ except socket.error, v:
+ if v[0] == 32: # Broken pipe
+ self.close()
+ raise
+
+class StreamingHTTPConnection(_StreamingHTTPMixin, httplib.HTTPConnection):
+ """Subclass of `httplib.HTTPConnection` that overrides the `send()` method
+ to support iterable body objects"""
+
+class StreamingHTTPRedirectHandler(urllib2.HTTPRedirectHandler):
+ """Subclass of `urllib2.HTTPRedirectHandler` that overrides the
+ `redirect_request` method to properly handle redirected POST requests
+
+ This class is required because python 2.5's HTTPRedirectHandler does
+ not remove the Content-Type or Content-Length headers when requesting
+ the new resource, but the body of the original request is not preserved.
+ """
+
+ handler_order = urllib2.HTTPRedirectHandler.handler_order - 1
+
+ # From python2.6 urllib2's HTTPRedirectHandler
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
+ """Return a Request or None in response to a redirect.
+
+ This is called by the http_error_30x methods when a
+ redirection response is received. If a redirection should
+ take place, return a new Request to allow http_error_30x to
+ perform the redirect. Otherwise, raise HTTPError if no-one
+ else should try to handle this url. Return None if you can't
+ but another Handler might.
+ """
+ m = req.get_method()
+ if (code in (301, 302, 303, 307) and m in ("GET", "HEAD")
+ or code in (301, 302, 303) and m == "POST"):
+ # Strictly (according to RFC 2616), 301 or 302 in response
+ # to a POST MUST NOT cause a redirection without confirmation
+ # from the user (of urllib2, in this case). In practice,
+ # essentially all clients do redirect in this case, so we
+ # do the same.
+ # be conciliant with URIs containing a space
+ newurl = newurl.replace(' ', '%20')
+ newheaders = dict((k, v) for k, v in req.headers.items()
+ if k.lower() not in (
+ "content-length", "content-type")
+ )
+ return urllib2.Request(newurl,
+ headers=newheaders,
+ origin_req_host=req.get_origin_req_host(),
+ unverifiable=True)
+ else:
+ raise urllib2.HTTPError(req.get_full_url(), code, msg, headers, fp)
+
+class StreamingHTTPHandler(urllib2.HTTPHandler):
+ """Subclass of `urllib2.HTTPHandler` that uses
+ StreamingHTTPConnection as its http connection class."""
+
+ handler_order = urllib2.HTTPHandler.handler_order - 1
+
+ def http_open(self, req):
+ """Open a StreamingHTTPConnection for the given request"""
+ return self.do_open(StreamingHTTPConnection, req)
+
+ def http_request(self, req):
+ """Handle a HTTP request. Make sure that Content-Length is specified
+ if we're using an interable value"""
+ # Make sure that if we're using an iterable object as the request
+ # body, that we've also specified Content-Length
+ if req.has_data():
+ data = req.get_data()
+ if hasattr(data, 'read') or hasattr(data, 'next'):
+ if not req.has_header('Content-length'):
+ raise ValueError(
+ "No Content-Length specified for iterable body")
+ return urllib2.HTTPHandler.do_request_(self, req)
+
+if hasattr(httplib, 'HTTPS'):
+ class StreamingHTTPSConnection(_StreamingHTTPMixin,
+ httplib.HTTPSConnection):
+ """Subclass of `httplib.HTTSConnection` that overrides the `send()`
+ method to support iterable body objects"""
+
+ class StreamingHTTPSHandler(urllib2.HTTPSHandler):
+ """Subclass of `urllib2.HTTPSHandler` that uses
+ StreamingHTTPSConnection as its http connection class."""
+
+ handler_order = urllib2.HTTPSHandler.handler_order - 1
+
+ def https_open(self, req):
+ return self.do_open(StreamingHTTPSConnection, req)
+
+ def https_request(self, req):
+ # Make sure that if we're using an iterable object as the request
+ # body, that we've also specified Content-Length
+ if req.has_data():
+ data = req.get_data()
+ if hasattr(data, 'read') or hasattr(data, 'next'):
+ if not req.has_header('Content-length'):
+ raise ValueError(
+ "No Content-Length specified for iterable body")
+ return urllib2.HTTPSHandler.do_request_(self, req)
+
+
+def register_openers():
+ """Register the streaming http handlers in the global urllib2 default
+ opener object.
+
+ Returns the created OpenerDirector object."""
+ handlers = [StreamingHTTPHandler, StreamingHTTPRedirectHandler]
+ if hasattr(httplib, "HTTPS"):
+ handlers.append(StreamingHTTPSHandler)
+
+ opener = urllib2.build_opener(*handlers)
+
+ urllib2.install_opener(opener)
+
+ return opener
diff --git a/src/libs/qt-breakpad/qtbreakpad.pri b/src/libs/qt-breakpad/qtbreakpad.pri
new file mode 100644
index 0000000000..4eada575c1
--- /dev/null
+++ b/src/libs/qt-breakpad/qtbreakpad.pri
@@ -0,0 +1,64 @@
+isEmpty(BREAKPAD_SOURCE_DIR): return()
+
+HEADERS += $$PWD/qtbreakpad/qtsystemexceptionhandler.h
+SOURCES += $$PWD/qtbreakpad/qtsystemexceptionhandler.cpp
+
+DEFINES += ENABLE_QT_BREAKPAD
+
+win32:BREAKPAD_SOURCE_DIR ~= s,\\,/,
+
+INCLUDEPATH += \
+ $$BREAKPAD_SOURCE_DIR/src \
+ $$PWD/qtbreakpad
+
+SOURCES += \
+ $$BREAKPAD_SOURCE_DIR/src/common/string_conversion.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/convert_UTF.c \
+ $$BREAKPAD_SOURCE_DIR/src/common/md5.cc
+
+linux:SOURCES += \
+ $$BREAKPAD_SOURCE_DIR/src/client/minidump_file_writer.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/log/log.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/handler/exception_handler.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/handler/minidump_descriptor.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/guid_creator.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/dump_writer_common/thread_info.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/dump_writer_common/ucontext_reader.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/minidump_writer/linux_dumper.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/minidump_writer/minidump_writer.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/microdump_writer/microdump_writer.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/file_id.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/elfutils.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/linux_libc_support.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/memory_mapped_file.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/linux/safe_readlink.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/linux/crash_generation/crash_generation_client.cc
+
+win32:SOURCES += \
+ $$BREAKPAD_SOURCE_DIR/src/common/windows/guid_string.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/windows/handler/exception_handler.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/windows/crash_generation/minidump_generator.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/windows/crash_generation/client_info.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/windows/crash_generation/crash_generation_client.cc
+
+macos {
+ SOURCES += \
+ $$BREAKPAD_SOURCE_DIR/src/client/minidump_file_writer.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/crash_generation/crash_generation_client.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/handler/exception_handler.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/handler/minidump_generator.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/handler/breakpad_nlist_64.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/handler/dynamic_images.cc \
+ $$BREAKPAD_SOURCE_DIR/src/client/mac/handler/protected_memory_allocator.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/bootstrap_compat.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/file_id.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/macho_id.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/macho_reader.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/macho_utilities.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/macho_walker.cc \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/string_utilities.cc
+ OBJECTIVE_SOURCES += \
+ $$BREAKPAD_SOURCE_DIR/src/common/mac/MachIPC.mm
+ LIBS += -framework Foundation
+}
diff --git a/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.cpp b/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.cpp
new file mode 100644
index 0000000000..d439ba112d
--- /dev/null
+++ b/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "qtsystemexceptionhandler.h"
+
+#include <utils/fileutils.h>
+#include <utils/hostosinfo.h>
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QProcess>
+
+#if defined(Q_OS_LINUX)
+# include "client/linux/handler/exception_handler.h"
+#elif defined(Q_OS_WIN)
+# include "client/windows/handler/exception_handler.h"
+#elif defined(Q_OS_MACOS)
+# include "client/mac/handler/exception_handler.h"
+#endif
+
+#if defined(Q_OS_LINUX)
+static bool exceptionHandlerCallback(const google_breakpad::MinidumpDescriptor& descriptor,
+ void* /*context*/,
+ bool succeeded)
+{
+ if (!succeeded)
+ return succeeded;
+
+ const QStringList argumentList = {
+ QString::fromLocal8Bit(descriptor.path()),
+ QString::number(QtSystemExceptionHandler::startTime().toTime_t()),
+ QCoreApplication::applicationName(),
+ QCoreApplication::applicationVersion(),
+ QtSystemExceptionHandler::plugins(),
+ QtSystemExceptionHandler::buildVersion(),
+ QCoreApplication::applicationFilePath()
+ };
+
+ return !QProcess::execute(QtSystemExceptionHandler::crashHandlerPath(), argumentList);
+}
+#elif defined(Q_OS_MACOS)
+static bool exceptionHandlerCallback(const char *dump_dir,
+ const char *minidump_id,
+ void *context,
+ bool succeeded)
+{
+ Q_UNUSED(context);
+
+ if (!succeeded)
+ return succeeded;
+
+ const QString path = QString::fromLocal8Bit(dump_dir) + '/'
+ + QString::fromLocal8Bit(minidump_id) + ".dmp";
+ const QStringList argumentList = {
+ path,
+ QString::number(QtSystemExceptionHandler::startTime().toTime_t()),
+ QCoreApplication::applicationName(),
+ QCoreApplication::applicationVersion(),
+ QtSystemExceptionHandler::plugins(),
+ QtSystemExceptionHandler::buildVersion(),
+ QCoreApplication::applicationFilePath()
+ };
+
+ return !QProcess::execute(QtSystemExceptionHandler::crashHandlerPath(), argumentList);
+}
+#elif defined(Q_OS_WIN)
+static bool exceptionHandlerCallback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded)
+{
+ Q_UNUSED(assertion);
+ Q_UNUSED(exinfo);
+ Q_UNUSED(context);
+
+ if (!succeeded)
+ return succeeded;
+
+ const QString path = QString::fromWCharArray(dump_path, int(wcslen(dump_path))) + '/'
+ + QString::fromWCharArray(minidump_id, int(wcslen(minidump_id))) + ".dmp";
+ const QStringList argumentList = {
+ path,
+ QString::number(QtSystemExceptionHandler::startTime().toTime_t()),
+ QCoreApplication::applicationName(),
+ QCoreApplication::applicationVersion(),
+ QtSystemExceptionHandler::plugins(),
+ QtSystemExceptionHandler::buildVersion(),
+ QCoreApplication::applicationFilePath()
+ };
+
+ return !QProcess::execute(QtSystemExceptionHandler::crashHandlerPath(), argumentList);
+}
+#endif
+
+static QDateTime s_startTime;
+static QString s_plugins;
+static QString s_buildVersion;
+static QString s_crashHandlerPath;
+
+#if defined(Q_OS_LINUX)
+QtSystemExceptionHandler::QtSystemExceptionHandler(const QString &libexecPath)
+ : exceptionHandler(new google_breakpad::ExceptionHandler(
+ google_breakpad::MinidumpDescriptor(QDir::tempPath().toStdString()),
+ NULL,
+ exceptionHandlerCallback,
+ NULL,
+ true,
+ -1))
+{
+ init(libexecPath);
+}
+#elif defined(Q_OS_MACOS)
+QtSystemExceptionHandler::QtSystemExceptionHandler(const QString &libexecPath)
+ : exceptionHandler(new google_breakpad::ExceptionHandler(
+ QDir::tempPath().toStdString(),
+ NULL,
+ exceptionHandlerCallback,
+ NULL,
+ true,
+ NULL))
+{
+ init(libexecPath);
+}
+#elif defined(Q_OS_WIN)
+QtSystemExceptionHandler::QtSystemExceptionHandler(const QString &libexecPath)
+ : exceptionHandler(new google_breakpad::ExceptionHandler(
+ QDir::tempPath().toStdWString(),
+ NULL,
+ exceptionHandlerCallback,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL))
+{
+ init(libexecPath);
+}
+#else
+QtSystemExceptionHandler::QtSystemExceptionHandler(const QString & /*libexecPath*/)
+ : exceptionHandler(0)
+{
+
+}
+#endif
+
+void QtSystemExceptionHandler::init(const QString &libexecPath)
+{
+ s_startTime = QDateTime::currentDateTime();
+ s_crashHandlerPath = libexecPath + Utils::HostOsInfo::withExecutableSuffix("/qtcrashhandler");
+}
+
+QtSystemExceptionHandler::~QtSystemExceptionHandler()
+{
+#ifdef ENABLE_QT_BREAKPAD
+ delete exceptionHandler;
+#endif
+}
+
+void QtSystemExceptionHandler::setPlugins(const QStringList &pluginNameList)
+{
+ s_plugins = QString("{%1}").arg(pluginNameList.join(","));
+}
+
+void QtSystemExceptionHandler::setBuildVersion(const QString &version)
+{
+ s_buildVersion = version;
+}
+
+QString QtSystemExceptionHandler::buildVersion()
+{
+ return s_buildVersion;
+}
+
+QString QtSystemExceptionHandler::plugins()
+{
+ return s_plugins;
+}
+
+void QtSystemExceptionHandler::setCrashHandlerPath(const QString &crashHandlerPath)
+{
+ s_crashHandlerPath = crashHandlerPath;
+}
+
+QString QtSystemExceptionHandler::crashHandlerPath()
+{
+ return s_crashHandlerPath;
+}
+
+void QtSystemExceptionHandler::crash()
+{
+ int *a = (int*)0x42;
+
+ fprintf(stdout, "Going to crash...\n");
+ fprintf(stdout, "A = %d", *a);
+}
+
+QDateTime QtSystemExceptionHandler::startTime()
+{
+ return s_startTime;
+}
diff --git a/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.h b/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.h
new file mode 100644
index 0000000000..2cc3067d77
--- /dev/null
+++ b/src/libs/qt-breakpad/qtbreakpad/qtsystemexceptionhandler.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QDateTime>
+
+namespace google_breakpad {
+ class ExceptionHandler;
+}
+
+class QtSystemExceptionHandler
+{
+public:
+ QtSystemExceptionHandler(const QString &libexecPath);
+ ~QtSystemExceptionHandler();
+
+ static void crash();
+ static void setPlugins(const QStringList &pluginNameList);
+ static void setBuildVersion(const QString &version);
+ static void setCrashHandlerPath(const QString &crashHandlerPath);
+
+ static QString plugins();
+ static QString buildVersion();
+ static QString crashHandlerPath();
+
+ static QDateTime startTime();
+
+protected:
+ void init(const QString &libexecPath);
+
+private:
+ google_breakpad::ExceptionHandler *exceptionHandler = nullptr;
+};
diff --git a/src/libs/qt-breakpad/qtbreakpadsymbols b/src/libs/qt-breakpad/qtbreakpadsymbols
new file mode 100755
index 0000000000..3b7c5af8ff
--- /dev/null
+++ b/src/libs/qt-breakpad/qtbreakpadsymbols
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+
+import sys
+import os
+import os.path
+import subprocess
+import shlex
+import shutil
+import base64
+
+from poster.encode import multipart_encode
+from poster.encode import MultipartParam
+from poster.streaminghttp import register_openers
+import urllib2
+
+from optparse import OptionParser
+
+register_openers()
+
+breakpadSourceDir = os.environ['BREAKPAD_SOURCE_DIR']
+breakpadUploadUrl = os.environ['BREAKPAD_UPLOAD_URL']
+breakpadUserName = os.environ['BREAKPAD_USER_NAME']
+breakpadUserPassword = os.environ['BREAKPAD_USER_PASSWORD']
+
+if sys.platform == 'win32':
+ nullfilename = 'nul:'
+else:
+ nullfilename = '/dev/null'
+nullfile = open(nullfilename, 'r+b')
+
+
+def stdoutFromProcess(process):
+ (stdout, stderr) = process.communicate()
+ if stderr:
+ print stderr
+ raise SystemError()
+ sys.exit(1)
+ return stdout
+
+def toolPath():
+ if sys.platform == 'linux2':
+ dumpsymsPath = os.path.join(breakpadSourceDir, 'src/tools/linux/dump_syms/dump_syms')
+ elif sys.platform == 'darwin':
+ dumpsymsPath = os.path.join(breakpadSourceDir, 'src/tools/mac/dump_syms/build/Release/dump_syms')
+ elif sys.platform == 'win32':
+ dumpsymsPath = os.path.join(breakpadSourceDir,'src\\tools\\windows\\binaries\\dump_syms.exe')
+ else:
+ sys.exit(1)
+ return dumpsymsPath
+
+gitRootDirectoryCache = {}
+
+def gitRootDirectory(path):
+ directory = os.path.dirname(path)
+ if directory in gitRootDirectoryCache:
+ return gitRootDirectoryCache[directory]
+ directoryList = directory.split(os.sep)
+ while len(directoryList) > 0:
+ gitDirectory = os.sep.join(directoryList + ['.git'])
+ if os.path.exists(gitDirectory):
+ gitDirectory = os.sep.join(directoryList)
+ gitRootDirectoryCache[directory] = gitDirectory
+ return gitDirectory
+ directoryList.pop()
+
+ return None
+
+
+gitCommitShaCache = {}
+
+
+def gitCommitSha(gitRootPath):
+ if gitRootPath in gitCommitShaCache:
+ return gitCommitShaCache[gitRootPath]
+ gitProcess = subprocess.Popen(shlex.split('git rev-parse HEAD'),
+ stdout=subprocess.PIPE,
+ stderr=nullfile, cwd=gitRootPath)
+ commitSha = stdoutFromProcess(gitProcess).strip('\n')
+ gitCommitShaCache[gitRootPath] = commitSha
+ return commitSha
+
+
+gitRemoteRepositoryCache = {}
+
+
+def gitRemoteRepository(gitRootPath):
+ if gitRootPath in gitRemoteRepositoryCache:
+ return gitRemoteRepositoryCache[gitRootPath]
+ gitProcess = \
+ subprocess.Popen(shlex.split('git config remote.origin.url'),
+ stdout=subprocess.PIPE, stderr=nullfile,
+ cwd=gitRootPath)
+ repository = stdoutFromProcess(gitProcess).strip('\n')
+ gitRemoteRepositoryCache[gitRootPath] = repository
+ return repository
+
+
+gitFilePathCache = {}
+
+
+def populateFilePathCache(gitRootPath):
+ if gitRootPath not in gitFilePathCache:
+ gitProcess = \
+ subprocess.Popen(shlex.split('git ls-files --full-name'),
+ stdout=subprocess.PIPE, stderr=nullfile,
+ cwd=gitRootPath)
+ fileNameList = stdoutFromProcess(gitProcess).split('\n')
+ filePathCache = {}
+ for fileName in fileNameList:
+ baseFileName = os.path.basename(fileName)
+ filePathCache[baseFileName] = fileName
+ gitFilePathCache[gitRootPath] = filePathCache
+
+
+def gitFilePath(path, gitRootPath):
+ if not gitRootPath:
+ return path
+
+ populateFilePathCache(gitRootPath)
+
+ baseFileName = os.path.basename(path)
+ filePathCache = gitFilePathCache[gitRootPath]
+ if baseFileName in filePathCache:
+ return filePathCache[baseFileName]
+ else:
+ return os.path.relpath(path, gitRootPath)
+
+
+def isInRepository(path):
+ gitRootPath = gitRootDirectory(path)
+ if not gitRootPath:
+ return False
+
+ populateFilePathCache(gitRootPath)
+ baseFileName = os.path.basename(path)
+ if baseFileName in gitFilePathCache[gitRootPath]:
+ return True
+ else:
+ return False
+
+
+def sendSymbolsToServer(
+ breakpadUploadUrl,
+ symbolText,
+ codeFile,
+ debugFile,
+ debugIdentifier,
+ operatingSystem,
+ cpu,
+ ):
+ (data, headers) = multipart_encode({
+ 'symbol_file': MultipartParam('symbol_file', value=symbolText,
+ filename='symbol_file'),
+ 'code_file': codeFile,
+ 'debug_file': debugFile,
+ 'debug_identifier': debugIdentifier,
+ 'os': operatingSystem,
+ 'cpu': cpu,
+ })
+ request = urllib2.Request(breakpadUploadUrl, data, headers)
+ auth = base64.encodestring('%s:%s' % (breakpadUserName,
+ breakpadUserPassword))[:-1] # This is just standard un/pw encoding
+ request.add_header('Authorization', 'Basic %s' % auth) # Add Auth header to request
+ result = urllib2.urlopen(request).read()
+
+
+def generateSymbolFilesAndSend(binaryPath, projectPath):
+ dumpsymsPath = toolPath()
+
+ originalBinaryPath = binaryPath
+
+ if sys.platform == 'darwin':
+ dsymutilProcess = \
+ subprocess.Popen(shlex.split('/usr/bin/dsymutil "'
+ + binaryPath + '"'), stdout=nullfile,
+ stderr=nullfile)
+ dsymutilProcess.wait()
+ binaryPath += os.path.join('.dSYM/Contents/Resources/DWARF/',
+ os.path.basename(binaryPath))
+
+ binaryPath = os.path.normpath(binaryPath)
+
+ dumpsymsProcess = subprocess.Popen(shlex.split('"' + dumpsymsPath
+ + '" "' + binaryPath + '"'), stdout=subprocess.PIPE,
+ stderr=nullfile, cwd=projectPath)
+ symbolList = stdoutFromProcess(dumpsymsProcess).split('\n')
+
+ outputTextList = []
+ codeFile = os.path.basename(binaryPath)
+ debugFile = ''
+ debugIdentifier = ''
+ operatingSystem = ''
+ cpu = ''
+ moduleNotParsed = True
+ for line in symbolList:
+ line = line.strip('\n').strip('\r')
+ if line[:4] == 'FILE':
+ (marker, idnumber, filepath) = line.split(' ', 2)
+ filepath = os.path.normpath(os.path.join(projectPath,
+ filepath))
+
+ if isInRepository(filepath):
+ gitRootPath = gitRootDirectory(filepath)
+ commitSha = gitCommitSha(gitRootPath)
+ repository = \
+ gitRemoteRepository(gitRootPath).replace(':', '/')
+ relativeFilePath = gitFilePath(filepath,
+ gitRootPath).replace('\\', '/')
+ outputTextList.append('FILE ' + idnumber + ' git:'
+ + repository + ':' + relativeFilePath + ':'
+ + commitSha)
+ else:
+ outputTextList.append(line)
+ elif moduleNotParsed and line[:6] == 'MODULE':
+ (operatingSystem, cpu, debugIdentifier, debugFile) = \
+ line[7:].split(' ', 3)
+ moduleNotParsed = False
+ elif line:
+ outputTextList.append(line)
+
+ if moduleNotParsed:
+ print 'Module not parsed'
+ sys.exit(1)
+
+ sendSymbolsToServer(
+ breakpadUploadUrl,
+ '\n'.join(outputTextList),
+ codeFile,
+ debugFile,
+ debugIdentifier,
+ operatingSystem,
+ cpu,
+ )
+
+ if sys.platform == 'darwin':
+ shutil.rmtree(originalBinaryPath + '.dSYM')
+
+
+def testForBreakpad():
+ try:
+ dumpsymsPath = toolPath()
+ if not dumpsymsPath:
+ sys.exit(1)
+ subprocess.Popen([dumpsymsPath], stdout=nullfile,
+ stderr=nullfile)
+ except (OSError, KeyError):
+ print 'No dumpsyms can be executed. Maybe BREAKPAD_SOURCE_DIR is wrong.'
+ sys.exit(1)
+ sys.exit(0)
+
+
+def main():
+ usage = 'usage: %prog [options] binary projectpath'
+ parser = OptionParser(usage=usage)
+ parser.add_option('-v', '--verbose', action='store_true',
+ dest='verbose')
+ parser.add_option('-e', '--breakpad-exists', action='store_true',
+ dest='testForBreakpad')
+
+ (options, args) = parser.parse_args()
+
+ if options.testForBreakpad == True:
+ testForBreakpad()
+ if len(args) > 1:
+ generateSymbolFilesAndSend(args[0], args[1])
+ else:
+ parser.print_help()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/src/libs/qt-breakpad/qtbreakpadsymbols.bat b/src/libs/qt-breakpad/qtbreakpadsymbols.bat
new file mode 100644
index 0000000000..aad26594d6
--- /dev/null
+++ b/src/libs/qt-breakpad/qtbreakpadsymbols.bat
@@ -0,0 +1 @@
+@python qtbreakpadsymbols %*
diff --git a/src/libs/qt-breakpad/qtcrashhandler.pri b/src/libs/qt-breakpad/qtcrashhandler.pri
new file mode 100644
index 0000000000..c452c09bd8
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler.pri
@@ -0,0 +1,21 @@
+isEmpty(BREAKPAD_SOURCE_DIR): return()
+
+QT += network
+
+INCLUDEPATH += \
+ $$BREAKPAD_SOURCE_DIR/src \
+ $$PWD/qtcrashhandler
+
+SOURCES += \
+ $$PWD/qtcrashhandler/main.cpp \
+ $$PWD/qtcrashhandler/mainwidget.cpp \
+ $$PWD/qtcrashhandler/detaildialog.cpp \
+ $$PWD/qtcrashhandler/dumpsender.cpp
+
+HEADERS += \
+ $$PWD/qtcrashhandler/mainwidget.h \
+ $$PWD/qtcrashhandler/detaildialog.h \
+ $$PWD/qtcrashhandler/dumpsender.h
+
+FORMS += \
+ $$PWD/qtcrashhandler/mainwidget.ui
diff --git a/src/libs/qt-breakpad/qtcrashhandler/detaildialog.cpp b/src/libs/qt-breakpad/qtcrashhandler/detaildialog.cpp
new file mode 100644
index 0000000000..21c7e87c89
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/detaildialog.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "detaildialog.h"
+
+#include <QDialogButtonBox>
+#include <QTextBrowser>
+#include <QVBoxLayout>
+
+DetailDialog::DetailDialog(QWidget *parent) :
+ QDialog(parent)
+{
+ resize(640, 480);
+ QVBoxLayout *verticalLayout = new QVBoxLayout(this);
+ textBrowser = new QTextBrowser(this);
+ verticalLayout->addWidget(textBrowser);
+ buttonBox = new QDialogButtonBox(this);
+ buttonBox->setOrientation(Qt::Horizontal);
+ buttonBox->setStandardButtons(QDialogButtonBox::Close);
+
+ verticalLayout->addWidget(buttonBox);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+DetailDialog::~DetailDialog()
+{
+}
+
+void DetailDialog::setText(const QString &text)
+{
+ textBrowser->setPlainText(text);
+}
diff --git a/src/libs/qt-breakpad/qtcrashhandler/detaildialog.h b/src/libs/qt-breakpad/qtcrashhandler/detaildialog.h
new file mode 100644
index 0000000000..9db319a4e8
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/detaildialog.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QTextBrowser;
+class QDialogButtonBox;
+QT_END_NAMESPACE
+
+class DetailDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit DetailDialog(QWidget *parent = nullptr);
+ ~DetailDialog();
+
+ void setText(const QString &text);
+
+private:
+ QTextBrowser *textBrowser = nullptr;
+ QDialogButtonBox *buttonBox = nullptr;
+};
diff --git a/src/libs/qt-breakpad/qtcrashhandler/dumpsender.cpp b/src/libs/qt-breakpad/qtcrashhandler/dumpsender.cpp
new file mode 100644
index 0000000000..5b0d1ac3d5
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/dumpsender.cpp
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "dumpsender.h"
+
+#include <QCoreApplication>
+#include <QDateTime>
+#include <QDebug>
+#include <QFile>
+#include <QFileInfo>
+#include <QHttpMultiPart>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QPair>
+#include <QProcess>
+#include <QStringList>
+#include <QTemporaryFile>
+#include <QUrl>
+
+static const QByteArray boundary = "12345cfdfzfsdfsdfassssaaadsd24324jxccxzzzzz98xzcz";
+
+DumpSender::DumpSender(QObject *parent) :
+ QObject(parent),
+ m_httpMultiPart(QHttpMultiPart::FormDataType)
+{
+ const QString dumpPath = QCoreApplication::arguments().at(1);
+ const QByteArray startupTime = QCoreApplication::arguments().at(2).toLocal8Bit();
+ const QByteArray applicationName = QCoreApplication::arguments().at(3).toLocal8Bit();
+ QByteArray applicationVersion = QCoreApplication::arguments().at(4).toLocal8Bit();
+ const QByteArray plugins = QCoreApplication::arguments().at(5).toLocal8Bit();
+ // QByteArray ideRevision = QCoreApplication::arguments().at(6).toLocal8Bit();
+ m_applicationFilePath = QCoreApplication::arguments().at(7);
+
+ if (applicationVersion.isEmpty())
+ applicationVersion = "1.0.0";
+
+ QFile dumpFile(dumpPath, this);
+ const bool isOpen = dumpFile.open(QIODevice::ReadOnly);
+ Q_ASSERT(isOpen);
+ Q_UNUSED(isOpen);
+
+ const QList<QPair<QByteArray, QByteArray> > pairList = {
+ { "StartupTime", startupTime },
+ { "Vendor", "Qt Project" },
+ { "InstallTime", "0" },
+ { "Add-ons", plugins },
+ { "BuildID", "" },
+ { "SecondsSinceLastCrash", "0" },
+ { "ProductName", applicationName },
+ { "URL", "" },
+ { "Theme", "" },
+ { "Version", applicationVersion },
+ { "CrashTime", QByteArray::number(QDateTime::currentDateTime().toTime_t()) },
+ { "Throttleable", "0" }
+ };
+
+ m_formData.append("--" + boundary + "\r\n");
+ for (const auto &pair : pairList) {
+ m_formData.append("Content-Disposition: form-data; name=\"" + pair.first + "\"\r\n\r\n");
+ m_formData.append(pair.second + "\r\n");
+ m_formData.append("--" + boundary + "\r\n");
+ }
+
+
+ QByteArray dumpArray = dumpFile.readAll();
+ m_formData.append("Content-Type: application/octet-stream\r\n");
+ m_formData.append("Content-Disposition: form-data; name=\"upload_file_minidump\"; filename=\""
+ + QFileInfo(dumpPath).baseName().toUtf8() + "\r\n");
+ m_formData.append("Content-Transfer-Encoding: binary\r\n\r\n");
+ m_formData.append(dumpArray);
+
+ m_formData.append("--" + boundary + "--\r\n");
+
+ for (const auto &pair : pairList) {
+ QHttpPart httpPart;
+ httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"" + pair.first + "\"");
+ httpPart.setBody(pair.second);
+ m_httpMultiPart.append(httpPart);
+ }
+
+ QHttpPart dumpPart;
+ dumpPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
+ dumpPart.setHeader(QNetworkRequest::ContentDispositionHeader,
+ "form-data; name=\"upload_file_minidump\"; filename=\""
+ + QFileInfo(dumpPath).baseName().toUtf8() + "\"");
+ dumpPart.setRawHeader("Content-Transfer-Encoding:", "binary");
+ dumpPart.setBody(dumpArray);
+ m_httpMultiPart.append(dumpPart);
+}
+
+void DumpSender::sendDumpAndQuit()
+{
+ QNetworkAccessManager *manager = new QNetworkAccessManager(this);
+
+ QNetworkRequest request(QUrl("http://crashes.qt.io/submit"));
+
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=" + boundary);
+
+ QList<QPair<QByteArray, QByteArray>> pairList;
+
+ if (!m_emailAddress.isEmpty())
+ pairList.append({ "Email", m_emailAddress.toLocal8Bit() });
+
+ if (!m_commentText.isEmpty())
+ pairList.append({ "Comments", m_commentText.toLocal8Bit() });
+
+ for (const auto &pair : pairList) {
+ m_formData.append("Content-Disposition: form-data; name=\"" + pair.first + "\"\r\n\r\n");
+ m_formData.append(pair.second + "\r\n");
+ m_formData.append("--" + boundary + "\r\n");
+ }
+
+ for (const auto &pair : pairList) {
+ QHttpPart httpPart;
+ httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"" + pair.first + "\"");
+ httpPart.setBody(pair.second);
+ m_httpMultiPart.append(httpPart);
+ }
+
+ QNetworkReply *reply = manager->post(request, &m_httpMultiPart);
+
+ m_httpMultiPart.setParent(reply);
+
+ connect(reply, &QNetworkReply::uploadProgress, this, &DumpSender::uploadProgress);
+ connect(reply, &QNetworkReply::finished, QCoreApplication::instance(), &QCoreApplication::quit);
+ connect(reply,
+ static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),
+ QCoreApplication::instance(), &QCoreApplication::quit);
+}
+
+void DumpSender::restartCrashedApplicationAndSendDump()
+{
+ QProcess::startDetached(m_applicationFilePath);
+ sendDumpAndQuit();
+}
+
+void DumpSender::restartCrashedApplication()
+{
+ QProcess::startDetached(m_applicationFilePath);
+ QCoreApplication::quit();
+}
+
+void DumpSender::setEmailAddress(const QString &email)
+{
+ m_emailAddress = email;
+}
+
+void DumpSender::setCommentText(const QString &comment)
+{
+ m_commentText = comment;
+}
+
+int DumpSender::dumperSize() const
+{
+ return m_formData.size();
+}
diff --git a/src/libs/qt-breakpad/qtcrashhandler/dumpsender.h b/src/libs/qt-breakpad/qtcrashhandler/dumpsender.h
new file mode 100644
index 0000000000..4a57a5e99d
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/dumpsender.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QHttpMultiPart>
+#include <QObject>
+
+class DumpSender : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DumpSender(QObject *parent = nullptr);
+
+ int dumperSize() const;
+
+ void sendDumpAndQuit();
+ void restartCrashedApplication();
+ void restartCrashedApplicationAndSendDump();
+ void setEmailAddress(const QString &email);
+ void setCommentText(const QString &comment);
+
+signals:
+ void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+
+private:
+ QHttpMultiPart m_httpMultiPart;
+ QByteArray m_formData;
+ QString m_applicationFilePath;
+ QString m_emailAddress;
+ QString m_commentText;
+};
diff --git a/src/libs/qt-breakpad/qtcrashhandler/main.cpp b/src/libs/qt-breakpad/qtcrashhandler/main.cpp
new file mode 100644
index 0000000000..7a307381cc
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/main.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "mainwidget.h"
+#include "dumpsender.h"
+
+#include <QApplication>
+#include <QFileInfo>
+#include <QHostInfo>
+#include <QNetworkProxyFactory>
+
+int main(int argc, char *argv[])
+{
+ QApplication application(argc, argv);
+
+ if (application.arguments().count() <= 1)
+ return 0;
+
+ const QString dumpPath = QApplication::arguments().at(1);
+ if (!QFileInfo(dumpPath).exists())
+ return 0;
+
+ QNetworkProxyFactory::setUseSystemConfiguration(true);
+
+ QHostInfo hostInfo = QHostInfo::fromName("crashes.qt.io");
+
+// if (hostInfo.error() != QHostInfo::NoError)
+// return 0;
+
+ DumpSender dumpSender;
+
+ MainWidget mainWindow;
+
+ mainWindow.setProgressbarMaximum(dumpSender.dumperSize());
+
+ QObject::connect(&mainWindow, &MainWidget::restartCrashedApplication,
+ &dumpSender, &DumpSender::restartCrashedApplication);
+ QObject::connect(&mainWindow, &MainWidget::restartCrashedApplicationAndSendDump,
+ &dumpSender, &DumpSender::restartCrashedApplicationAndSendDump);
+ QObject::connect(&mainWindow, &MainWidget::sendDump,
+ &dumpSender, &DumpSender::sendDumpAndQuit);
+ QObject::connect(&mainWindow, &MainWidget::commentChanged,
+ &dumpSender, &DumpSender::setCommentText);
+ QObject::connect(&mainWindow, &MainWidget::emailAdressChanged,
+ &dumpSender, &DumpSender::setEmailAddress);
+ QObject::connect(&dumpSender, &DumpSender::uploadProgress,
+ &mainWindow, &MainWidget::updateProgressBar);
+
+ mainWindow.show();
+ return application.exec();
+}
diff --git a/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp
new file mode 100644
index 0000000000..0fc4f2e1d4
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "mainwidget.h"
+#include "ui_mainwidget.h"
+
+#include <QApplication>
+#include <QDateTime>
+#include <QFile>
+#include <QFileInfo>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QPair>
+#include <QStringList>
+#include <QTemporaryFile>
+#include <QUrl>
+
+MainWidget::MainWidget(QWidget *parent) :
+ QWidget(parent),
+ ui(new Ui::MainWidget)
+{
+ ui->setupUi(this);
+
+ connect(ui->restartButton, &QAbstractButton::clicked, this, &MainWidget::restartApplication);
+ connect(ui->quitButton, &QAbstractButton::clicked, this, &MainWidget::quitApplication);
+ connect(ui->detailButton, &QAbstractButton::clicked, this, &MainWidget::showDetails);
+ connect(ui->commentTextEdit, &QTextEdit::textChanged, this, &MainWidget::commentIsProvided);
+ connect(ui->emailLineEdit, &QLineEdit::textEdited, this, &MainWidget::emailAdressChanged);
+}
+
+MainWidget::~MainWidget()
+{
+ delete ui;
+}
+
+void MainWidget::setProgressbarMaximum(int maximum)
+{
+ ui->progressBar->setMaximum(maximum);
+}
+
+void MainWidget::changeEvent(QEvent *e)
+{
+ QWidget::changeEvent(e);
+ if (e->type() == QEvent::LanguageChange)
+ ui->retranslateUi(this);
+}
+
+void MainWidget::updateProgressBar(qint64 progressCount, qint64 fullCount)
+{
+ ui->progressBar->setValue(static_cast<int>(progressCount));
+ ui->progressBar->setMaximum(static_cast<int>(fullCount));
+}
+
+void MainWidget::showError(QNetworkReply::NetworkError error)
+{
+ QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
+ if (error != QNetworkReply::NoError && reply) {
+ ui->commentTextEdit->setReadOnly(true);
+ ui->commentTextEdit->setPlainText(reply->errorString());
+ }
+}
+
+void MainWidget::restartApplication()
+{
+ if (ui->sendDumpCheckBox->isChecked())
+ emit restartCrashedApplicationAndSendDump();
+ else
+ emit restartCrashedApplication();
+}
+
+void MainWidget::quitApplication()
+{
+ ui->quitButton->setEnabled(false);
+ if (ui->sendDumpCheckBox->isChecked())
+ emit sendDump();
+ else
+ QCoreApplication::quit();
+}
+
+void MainWidget::commentIsProvided()
+{
+ m_commentIsProvided = true;
+ emit commentChanged(ui->commentTextEdit->toPlainText());
+}
+
+void MainWidget::showDetails()
+{
+ if (m_detailDialog.isNull()) {
+ m_detailDialog = new DetailDialog(this);
+
+ QString detailText;
+
+ detailText.append(tr("We specifically send the following information:\n\n"));
+
+ QString dumpPath = QApplication::arguments().at(1);
+ QString startupTime = QApplication::arguments().at(2);
+ QString applicationName = QApplication::arguments().at(3);
+ QString applicationVersion = QApplication::arguments().at(4);
+ QString plugins = QApplication::arguments().at(5);
+ QString ideRevision = QApplication::arguments().at(6);
+
+ detailText.append(QString("StartupTime: %1\n").arg(startupTime));
+ detailText.append(QString("Vendor: %1\n").arg("Qt Project"));
+ detailText.append(QString("InstallTime: %1\n").arg("0"));
+ detailText.append(QString("Add-ons: %1\n").arg(plugins));
+ detailText.append(QString("BuildID: %1\n").arg("0"));
+ detailText.append(QString("SecondsSinceLastCrash: %1\n").arg("0"));
+ detailText.append(QString("ProductName: %1\n").arg(applicationName));
+ detailText.append(QString("URL: %1\n").arg(""));
+ detailText.append(QString("Theme: %1\n").arg(""));
+ detailText.append(QString("Version: %1\n").arg(applicationVersion));
+ detailText.append(QString("CrashTime: %1\n").arg(QString::number(QDateTime::currentDateTime().toTime_t())));
+
+ if (!ui->emailLineEdit->text().isEmpty())
+ detailText.append(tr("Email: %1\n").arg(ui->emailLineEdit->text()));
+
+ if (m_commentIsProvided)
+ detailText.append(tr("Comments: %1\n").arg(ui->commentTextEdit->toPlainText()));
+
+ detailText.append(
+ tr("In addition, we send a Microsoft Minidump file, which contains information "
+ "about this computer, such as the operating system and CPU, and most "
+ "importantly, it contains the stacktrace, which is an internal structure that "
+ "shows where the program crashed. This information will help us to identify "
+ "the cause of the crash and to fix it."));
+
+ m_detailDialog.data()->setText(detailText);
+ }
+ if (m_detailDialog->isVisible())
+ m_detailDialog->showNormal();
+ else
+ m_detailDialog->show();
+}
diff --git a/src/libs/qt-breakpad/qtcrashhandler/mainwidget.h b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.h
new file mode 100644
index 0000000000..4e3b9880c1
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include "detaildialog.h"
+
+#include <QByteArray>
+#include <QNetworkReply>
+#include <QPointer>
+#include <QWidget>
+
+namespace Ui { class MainWidget; }
+
+class MainWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit MainWidget(QWidget *parent = nullptr);
+ ~MainWidget();
+
+ void setProgressbarMaximum(int maximum);
+ void updateProgressBar(qint64 progressCount, qint64 fullCount);
+
+signals:
+ void restartCrashedApplication();
+ void sendDump();
+ void restartCrashedApplicationAndSendDump();
+ void emailAdressChanged(const QString &email);
+ void commentChanged(const QString &comment);
+
+protected:
+ void changeEvent(QEvent *e);
+
+private:
+ void restartApplication();
+ void quitApplication();
+ void showError(QNetworkReply::NetworkError error);
+ void showDetails();
+ void commentIsProvided();
+
+private:
+ Ui::MainWidget *ui;
+
+ QPointer<DetailDialog> m_detailDialog;
+ bool m_commentIsProvided = false;
+};
diff --git a/src/libs/qt-breakpad/qtcrashhandler/mainwidget.ui b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.ui
new file mode 100644
index 0000000000..f509a70569
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/mainwidget.ui
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWidget</class>
+ <widget class="QWidget" name="MainWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>422</width>
+ <height>510</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Crash Handler</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <pointsize>20</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>Qt Creator has crashed</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>You can send us a crash report in order to help us diagnose and fix the problem.</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Email:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="emailLineEdit">
+ <property name="placeholderText">
+ <string>Enter here your email (optional)</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="sendDumpCheckBox">
+ <property name="text">
+ <string>Tell The Qt Company about this crash so they can fix it</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="detailButton">
+ <property name="text">
+ <string>Details</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="commentTextEdit">
+ <property name="overwriteMode">
+ <bool>true</bool>
+ </property>
+ <property name="placeholderText">
+ <string>Please describe what you did before it crashed (comments are publicly visible)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Your crash report will be submitted before you quit or restart.</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QProgressBar" name="progressBar">
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="format">
+ <string>%v/%m Bytes</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="restartButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Restart</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="quitButton">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/libs/qt-breakpad/qtcrashhandler/qtcrashhandler.pro b/src/libs/qt-breakpad/qtcrashhandler/qtcrashhandler.pro
new file mode 100644
index 0000000000..f4bca4e322
--- /dev/null
+++ b/src/libs/qt-breakpad/qtcrashhandler/qtcrashhandler.pro
@@ -0,0 +1,6 @@
+TARGET = qtcrashhandler
+QT += network
+TEMPLATE = app
+
+include(../qtcrashhandler.pri)
+DESTDIR = ../bin
diff --git a/src/libs/qt-breakpad/testapp/main.cpp b/src/libs/qt-breakpad/testapp/main.cpp
new file mode 100644
index 0000000000..1f21f86e4b
--- /dev/null
+++ b/src/libs/qt-breakpad/testapp/main.cpp
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include <qtsystemexceptionhandler.h>
+
+#include <QCoreApplication>
+#include <QTimer>
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ a.setApplicationName("testapp");
+ a.setApplicationVersion("1.0.0");
+
+ QtSystemExceptionHandler exceptionHandler;
+
+ QTimer::singleShot(100, [] { QtSystemExceptionHandler::crash(); });
+
+ return a.exec();
+}
diff --git a/src/libs/qt-breakpad/testapp/testapp.pro b/src/libs/qt-breakpad/testapp/testapp.pro
new file mode 100644
index 0000000000..407ad1e2ea
--- /dev/null
+++ b/src/libs/qt-breakpad/testapp/testapp.pro
@@ -0,0 +1,14 @@
+QT -= gui
+
+include(../qtbreakpad.pri)
+
+unix:TARGET = testapp.bin
+win32:TARGET = testapp
+
+## install_name_tool -change libQtBreakpad.1.dylib @executable_path/../lib/qtbreakpad/libQtBreakpad.dylib
+
+DESTDIR = ../bin
+
+CONFIG += console release c++11
+
+SOURCES += main.cpp
diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppet.pro b/src/tools/qml2puppet/qml2puppet/qml2puppet.pro
index 9644cf4bca..af8591e8ec 100644
--- a/src/tools/qml2puppet/qml2puppet/qml2puppet.pro
+++ b/src/tools/qml2puppet/qml2puppet/qml2puppet.pro
@@ -1,7 +1,7 @@
TARGET = qml2puppet
TEMPLATE = app
-
+QTC_LIB_DEPENDS += utils
include(../../../../qtcreator.pri)
osx: DESTDIR = $$IDE_LIBEXEC_PATH/qmldesigner
@@ -9,6 +9,7 @@ else: DESTDIR = $$IDE_LIBEXEC_PATH
include(../../../rpath.pri)
+include(../../../libs/qt-breakpad/qtbreakpad.pri)
include(../../../../share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri)
isEmpty(PRECOMPILED_HEADER):PRECOMPILED_HEADER = $$PWD/../../../shared/qtcreator_pch.h
diff --git a/src/tools/qtcrashhandler/qtcrashhandler.pro b/src/tools/qtcrashhandler/qtcrashhandler.pro
index f6ba7590ae..f2a75957d5 100644
--- a/src/tools/qtcrashhandler/qtcrashhandler.pro
+++ b/src/tools/qtcrashhandler/qtcrashhandler.pro
@@ -1,9 +1,6 @@
TARGET = qtcrashhandler
TEMPLATE = app
-QT_BREAKPAD_ROOT_PATH = $$(QT_BREAKPAD_ROOT_PATH)
-include($${QT_BREAKPAD_ROOT_PATH}/qtcrashhandler.pri)
-include(../../../qtcreator.pri)
-DESTDIR = $$IDE_BIN_PATH
-include(../../rpath.pri)
+include(../../qtcreatortool.pri)
+include(../../libs/qt-breakpad/qtcrashhandler.pri)
diff --git a/src/tools/tools.pro b/src/tools/tools.pro
index cfe34abaa0..23268020b7 100644
--- a/src/tools/tools.pro
+++ b/src/tools/tools.pro
@@ -41,8 +41,7 @@ isEmpty(BUILD_CPLUSPLUS_TOOLS):BUILD_CPLUSPLUS_TOOLS=$$(BUILD_CPLUSPLUS_TOOLS)
cplusplus-update-frontend
}
-QT_BREAKPAD_ROOT_PATH = $$(QT_BREAKPAD_ROOT_PATH)
-!isEmpty(QT_BREAKPAD_ROOT_PATH) {
+!isEmpty(BREAKPAD_SOURCE_DIR) {
SUBDIRS += qtcrashhandler
} else {
linux-* {