From c8a61cc8f2dbc155184c80fb5846ea9dc8641e2a Mon Sep 17 00:00:00 2001
From: hjk <qtc-committer@nokia.com>
Date: Thu, 11 Mar 2010 18:55:52 +0100
Subject: [PATCH] debugger: implement selected of string encoding per pointer
 type/individual pointer

---
 share/qtcreator/gdbmacros/dumper.py           | 148 +++++++++++++-----
 src/plugins/debugger/gdb/classicgdbengine.cpp |  13 +-
 src/plugins/debugger/gdb/gdbengine.cpp        |  20 +--
 src/plugins/debugger/gdb/pythongdbengine.cpp  |  12 +-
 src/plugins/debugger/watchhandler.cpp         | 100 +++++++++---
 src/plugins/debugger/watchhandler.h           |   5 +-
 src/plugins/debugger/watchutils.cpp           |  35 ++++-
 src/plugins/debugger/watchwindow.cpp          |   9 +-
 tests/manual/gdbdebugger/simple/app.cpp       |   2 +
 9 files changed, 244 insertions(+), 100 deletions(-)

diff --git a/share/qtcreator/gdbmacros/dumper.py b/share/qtcreator/gdbmacros/dumper.py
index 79402c00f48..26328f5c1bb 100644
--- a/share/qtcreator/gdbmacros/dumper.py
+++ b/share/qtcreator/gdbmacros/dumper.py
@@ -432,12 +432,50 @@ def qtNamespace():
         # Happens for none-Qt applications
         return ""
 
-def encodeCharArray(p, size):
+def findFirstZero(p, max):
+    for i in xrange(max):
+        if p.dereference() == 0:
+            return i
+        p = p + 1
+    return -1
+
+def encodeCharArray(p, maxsize):
+    t = gdb.lookup_type("unsigned char").pointer()
+    p = p.cast(t)
+    i = findFirstZero(p, maxsize)
+    limit = select(i < 0, maxsize, i)
     s = ""
-    p = p.cast(gdb.lookup_type("unsigned char").pointer())
-    for i in xrange(size):
+    for i in xrange(limit):
         s += "%02x" % int(p.dereference())
         p += 1
+    if i == maxsize:
+        s += "2e2e2e"
+    return s
+
+def encodeChar2Array(p, maxsize):
+    t = gdb.lookup_type("unsigned short").pointer()
+    p = p.cast(t)
+    i = findFirstZero(p, maxsize)
+    limit = select(i < 0, maxsize, i)
+    s = ""
+    for i in xrange(limit):
+        s += "%04x" % int(p.dereference())
+        p += 1
+    if i == maxsize:
+        s += "2e002e002e00"
+    return s
+
+def encodeChar4Array(p, maxsize):
+    t = gdb.lookup_type("unsigned int").pointer()
+    p = p.cast(t)
+    i = findFirstZero(p, maxsize)
+    limit = select(i < 0, maxsize, i)
+    s = ""
+    for i in xrange(limit):
+        s += "%08x" % int(p.dereference())
+        p += 1
+    if i == maxsize:
+        s += "2e0000002e0000002e000000"
     return s
 
 def encodeByteArray(value):
@@ -450,10 +488,7 @@ def encodeByteArray(value):
     if size > 0:
         checkAccess(data, 4)
         checkAccess(data + size) == 0
-
-    innerType = gdb.lookup_type("char")
-    p = gdb.Value(data.cast(innerType.pointer()))
-    return encodeCharArray(p, size)
+    return encodeCharArray(data, size)
 
 def encodeString(value):
     d_ptr = value['d'].dereference()
@@ -502,22 +537,33 @@ class FrameCommand(gdb.Command):
     def __init__(self):
         super(FrameCommand, self).__init__("bb", gdb.COMMAND_OBSCURE)
 
-    def invoke(self, arg, from_tty):
-        args = arg.split(' ')
-
-        #warn("ARG: %s" % arg)
-        #warn("ARGS: %s" % args)
-        options = args[0].split(",")
-        varList = args[1][1:]
-        if len(varList) == 0:
-            varList = []
-        else:
-            varList = varList.split(",")
-        expandedINames = set(args[2].split(","))
+    def invoke(self, args, from_tty):
+        options = []
+        varList = []
+        typeformats = {}
+        formats = {}
         watchers = ""
-        if len(args) > 3:
-            watchers = base64.b16decode(args[3], True)
-        #warn("WATCHERS: %s" % watchers)
+        for arg in args.split(' '):
+            pos = arg.find(":") + 1
+            if arg.startswith("options:"):
+                options = arg[pos:].split(",")
+            elif arg.startswith("vars:"):
+                vars = arg[pos:].split(",")
+            elif arg.startswith("expanded:"):
+                expandedINames = set(arg[pos:].split(","))
+            elif arg.startswith("typeformats:"):
+                for f in arg[pos:].split(","):
+                    pos = f.find("=")
+                    if pos != -1:
+                        type = base64.b16decode(f[0:pos], True)
+                        typeformats[type] = int(f[pos+1:])
+            elif arg.startswith("formats:"):
+                for f in arg[pos:].split(","):
+                    pos = f.find("=")
+                    if pos != -1:
+                        formats[f[0:pos]] = int(f[pos+1:])
+            elif arg.startswith("watchers:"):
+                watchers = base64.b16decode(arg[pos:], True)
 
         useFancy = "fancy" in options
 
@@ -552,6 +598,8 @@ class FrameCommand(gdb.Command):
 
         d = Dumper()
         d.dumpers = self.dumpers
+        d.typeformats = typeformats
+        d.formats = formats
         d.useFancy = useFancy
         d.passExceptions = "passexceptions" in options
         d.autoDerefPointers = "autoderef" in options
@@ -837,6 +885,9 @@ class Dumper:
         if len(type) > 0 and type != self.childTypes[-1]:
             self.put('type="%s",' % type) # str(type.unqualified()) ?
 
+    def putAddress(self, addr):
+        self.put('addr="%s",' % cleanAddress(addr))
+
     def putNumChild(self, numchild):
         #warn("NUM CHILD: '%s' '%s'" % (numchild, self.childNumChilds[-1]))
         if numchild != self.childNumChilds[-1]:
@@ -1038,13 +1089,36 @@ class Dumper:
 
 
         elif type.code == gdb.TYPE_CODE_PTR:
-            #warn("A POINTER: %s" % value.type)
             isHandled = False
 
-            if str(type.strip_typedefs()).find("(") != -1:
+            format = self.formats.get(item.iname)
+            if format is None:
+                format = self.typeformats.get(str(value.type))
+
+            if not format is None:
+                self.putAddress(value.address)
+                self.putType(item.value.type)
+                self.putNumChild(0)
+                isHandled = True
+
+            if format == 0:
+                # Bald pointer.
+                self.putValue(str(cleanAddress(value.address)))
+            elif format == 1 or format == 2:
+                # Latin1 or UTF-8
+                f = select(format == 1, "6", "9")
+                self.putValue(encodeCharArray(value, 100), f)
+            elif format == 3:
+                # UTF-16.
+                self.putValue(encodeChar2Array(value, 100), "11")
+            elif format == 4:
+                # UCS-4:
+                self.putValue(encodeChar4Array(value, 100), "10")
+
+            if (not isHandled) and str(type.strip_typedefs()).find("(") != -1:
                 # A function pointer.
                 self.putValue(str(item.value))
-                self.put('addr="%s",' % cleanAddress(value.address))
+                self.putAddress(value.address)
                 self.putType(item.value.type)
                 self.putNumChild(0)
                 isHandled = True
@@ -1069,21 +1143,8 @@ class Dumper:
                     # Display values up to given length directly
                     #warn("CHAR AUTODEREF: %s" % value.address)
                     self.putType(item.value.type)
-                    firstNul = -1
-                    p = value
-                    found = False
-                    for i in xrange(100):
-                        if p.dereference() == 0:
-                            # Found terminating NUL
-                            found = True
-                            break
-                        p += 1
-                    if found:
-                        self.putValue(encodeCharArray(value, i), "6")
-                        self.putNumChild(0)
-                    else:
-                        self.putValue(encodeCharArray(value, 100) + "2e2e2e", "6")
-                        self.putNumChild(0)
+                    self.putValue(encodeCharArray(value, 100), "6")
+                    self.putNumChild(0)
                     isHandled = True
 
             #warn("AUTODEREF: %s" % self.autoDerefPointers)
@@ -1105,8 +1166,9 @@ class Dumper:
             if not isHandled:
                 #warn("GENERIC PLAIN POINTER: %s" % value.type)
                 self.putType(item.value.type)
-                self.putValue(str(value))
-                self.put('addr="%s",' % cleanAddress(value.address))
+                #self.putValue(str(value))
+                self.putValue("")
+                self.putAddress(value.address)
                 self.putNumChild(1)
                 if self.isExpanded(item):
                     self.beginChildren()
@@ -1208,7 +1270,7 @@ class Dumper:
                 value = item.value[field.name]
                 child = Item(value, item.iname, field.name, field.name)
                 self.beginHash()
-                self.put('addr="%s",' % cleanAddress(value.address))
+                self.putAddress(value.address)
                 self.putItemHelper(child)
                 self.endHash();
             else:
diff --git a/src/plugins/debugger/gdb/classicgdbengine.cpp b/src/plugins/debugger/gdb/classicgdbengine.cpp
index f366cff8094..5d8cd936ab6 100644
--- a/src/plugins/debugger/gdb/classicgdbengine.cpp
+++ b/src/plugins/debugger/gdb/classicgdbengine.cpp
@@ -252,10 +252,10 @@ void GdbEngine::updateSubItemClassic(const WatchData &data0)
         return;
     }
 
-    // we should have a type now. this is relied upon further below
+    // We should have a type now. This is relied upon further below.
     QTC_ASSERT(!data.type.isEmpty(), return);
 
-    // a common case that can be easily solved
+    // A common case that can be easily solved.
     if (data.isChildrenNeeded() && isPointerType(data.type)
         && !hasDebuggingHelperForType(data.type)) {
         // We sometimes know what kind of children pointers have
@@ -431,8 +431,6 @@ void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response)
         while (out.endsWith(' ') || out.endsWith('\n'))
             out.chop(1);
         QList<QByteArray> list = out.split(' ');
-        //qDebug() << "RECEIVED" << response.toString() << "FOR" << data0.toString()
-        //    <<  " STREAM:" << out;
         if (list.isEmpty()) {
             //: Value for variable
             data.setError(WatchData::msgNotInScope());
@@ -538,7 +536,7 @@ void GdbEngine::tryLoadDebuggingHelpersClassic()
     postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
                 + "\", " + flag + ")",
         CB(handleDebuggingHelperSetup));
-    // some older systems like CentOS 4.6 prefer this:
+    // Some older systems like CentOS 4.6 prefer this:
     postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib)
                 + "\", " + flag + ")",
         CB(handleDebuggingHelperSetup));
@@ -550,7 +548,7 @@ void GdbEngine::tryLoadDebuggingHelpersClassic()
 void GdbEngine::tryQueryDebuggingHelpersClassic()
 {
     PRECONDITION;
-    // retrieve list of dumpable classes
+    // Retrieve list of dumpable classes.
     postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
     postCommand("p (char*)&qDumpOutBuffer",
         CB(handleQueryDebuggingHelperClassic));
@@ -560,7 +558,7 @@ void GdbEngine::recheckDebuggingHelperAvailabilityClassic()
 {
     PRECONDITION;
     if (m_gdbAdapter->dumperHandling() != AbstractGdbAdapter::DumperNotAvailable) {
-        // retrieve list of dumpable classes
+        // Retrieve list of dumpable classes.
         postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
         postCommand("p (char*)&qDumpOutBuffer",
             CB(handleQueryDebuggingHelperClassic));
@@ -681,7 +679,6 @@ bool GdbEngine::checkDebuggingHelpersClassic()
     if (!manager()->qtDumperLibraryEnabled())
         return false;
     const QString lib = qtDumperLibraryName();
-    //qDebug() << "DUMPERLIB:" << lib;
     const QFileInfo fi(lib);
     if (!fi.exists()) {
         const QStringList &locations = manager()->qtDumperLibraryLocations();
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 60088b3ef2c..50817196b3b 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -3321,7 +3321,7 @@ void GdbEngine::setWatchDataDisplayedType(WatchData &data, const GdbMi &item)
 void GdbEngine::handleVarCreate(const GdbResponse &response)
 {
     WatchData data = response.cookie.value<WatchData>();
-    // happens e.g. when we already issued a var-evaluate command
+    // Happens e.g. when we already issued a var-evaluate command.
     if (!data.isValid())
         return;
     //qDebug() << "HANDLE VARIABLE CREATION:" << data.toString();
@@ -3351,11 +3351,9 @@ void GdbEngine::handleVarCreate(const GdbResponse &response)
 
 void GdbEngine::handleDebuggingHelperSetup(const GdbResponse &response)
 {
-    //qDebug() << "CUSTOM SETUP RESULT:" << response.toString();
     if (response.resultClass == GdbResultDone) {
     } else {
         QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
-        //qDebug() << "CUSTOM DUMPER SETUP ERROR MESSAGE:" << msg;
         showStatusMessage(tr("Custom dumper setup: %1").arg(msg), 10000);
     }
 }
@@ -3381,7 +3379,6 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
     }
     setWatchDataType(data, item.findChild("type"));
     setWatchDataEditValue(data, item.findChild("editvalue"));
-    setWatchDataChildCount(data, item.findChild("numchild"));
     setWatchDataValue(data, item.findChild("value"),
         item.findChild("valueencoded").data().toInt());
     setWatchDataAddress(data, item.findChild("addr"));
@@ -3391,6 +3388,7 @@ void GdbEngine::handleChildren(const WatchData &data0, const GdbMi &item,
         item.findChild("valuetooltipencoded").data().toInt());
     setWatchDataValueEnabled(data, item.findChild("valueenabled"));
     setWatchDataValueEditable(data, item.findChild("valueeditable"));
+    setWatchDataChildCount(data, item.findChild("numchild"));
     //qDebug() << "\nAPPEND TO LIST: " << data.toString() << "\n";
     list->append(data);
 
@@ -3451,7 +3449,7 @@ void GdbEngine::updateLocals(const QVariant &cookie)
 }
 
 
-// Parse a local variable from GdbMi
+// Parse a local variable from GdbMi.
 WatchData GdbEngine::localVariable(const GdbMi &item,
                                    const QStringList &uninitializedVariables,
                                    QMap<QByteArray, int> *seen)
@@ -3479,7 +3477,7 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
         WatchData data;
         QString nam = _(name);
         data.iname = "local." + name + QByteArray::number(n + 1);
-        //: Variable %1 is the variable name, %2 is a simple count
+        //: Variable %1 is the variable name, %2 is a simple count.
         data.name = WatchData::shadowedName(nam, n);
         if (uninitializedVariables.contains(data.name)) {
             data.setError(WatchData::msgNotInScope());
@@ -3511,8 +3509,8 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
         // somewhere in the response.
         data.setChildrenUnneeded();
     } else {
-        // set value only directly if it is simple enough, otherwise
-        // pass through the insertData() machinery
+        // Set value only directly if it is simple enough, otherwise
+        // pass through the insertData() machinery.
         if (isIntOrFloatType(data.type) || isPointerType(data.type))
             setWatchDataValue(data, item.findChild("value"));
         if (isSymbianIntType(data.type)) {
@@ -3523,7 +3521,11 @@ WatchData GdbEngine::localVariable(const GdbMi &item,
 
     if (!m_manager->watchHandler()->isExpandedIName(data.iname))
         data.setChildrenUnneeded();
-    if (isPointerType(data.type) || data.name == __("this"))
+
+    GdbMi t = item.findChild("numchild");
+    if (t.isValid())
+        data.setHasChildren(t.data().toInt() > 0);
+    else if (isPointerType(data.type) || data.name == __("this"))
         data.setHasChildren(true);
     return data;
 }
diff --git a/src/plugins/debugger/gdb/pythongdbengine.cpp b/src/plugins/debugger/gdb/pythongdbengine.cpp
index 934eaa4500f..3bb164365bd 100644
--- a/src/plugins/debugger/gdb/pythongdbengine.cpp
+++ b/src/plugins/debugger/gdb/pythongdbengine.cpp
@@ -55,13 +55,7 @@ void GdbEngine::updateLocalsPython(const QByteArray &varList)
     //m_toolTipExpression.clear();
     WatchHandler *handler = m_manager->watchHandler();
 
-    QByteArray expanded;
-    QSet<QByteArray> expandedINames = handler->expandedINames();
-    QSetIterator<QByteArray> jt(expandedINames);
-    while (jt.hasNext()) {
-        expanded.append(jt.next());
-        expanded.append(',');
-    }
+    QByteArray expanded = handler->formatRequests();
     if (expanded.isEmpty())
         expanded.append("defaults,");
     expanded.chop(1);
@@ -92,8 +86,8 @@ void GdbEngine::updateLocalsPython(const QByteArray &varList)
         options += "defaults,";
     options.chop(1);
 
-    postCommand("bb " + options + " @" + varList + ' '
-            + expanded + ' ' + watchers.toHex(),
+    postCommand("bb options:" + options + " vars:" + varList + ' '
+            + expanded + " watchers:" + watchers.toHex(),
         WatchUpdate, CB(handleStackFramePython));
 }
 
diff --git a/src/plugins/debugger/watchhandler.cpp b/src/plugins/debugger/watchhandler.cpp
index 34cc848d644..c02d379726e 100644
--- a/src/plugins/debugger/watchhandler.cpp
+++ b/src/plugins/debugger/watchhandler.cpp
@@ -639,27 +639,6 @@ static QString formattedValue(const WatchData &data, int format)
             return reformatInteger(data.value.toULongLong(), format);
         return reformatInteger(data.value.toLongLong(), format);
     }
-    if (0 && !data.addr.isEmpty()) {
-        if (format == BaldPointerFormat)
-            return data.value;
-        bool ok = false;
-        const void *addr =
-            reinterpret_cast<void *>(data.value.toULongLong(&ok, 0));
-        if (!ok || !addr)
-            return data.value;
-        // FIXME: add a round trip through the debugger to prevent crashs?
-        if (format == Latin1StringFormat)
-            return QString::fromLatin1(static_cast<const char *>(addr));
-        if (format == Local8BitStringFormat)
-            return QString::fromLocal8Bit(static_cast<const char *>(addr));
-        if (format == Utf8StringFormat)
-            return QString::fromUtf8(static_cast<const char *>(addr));
-        if (format == Utf16StringFormat)
-            return QString::fromUtf16(static_cast<const ushort *>(addr));
-        if (format == Ucs4StringFormat)
-            return QString::fromUcs4(static_cast<const uint *>(addr));
-         return data.value;
-    }
     return data.value;
 }
 
@@ -802,7 +781,6 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
                     int format = m_handler->m_individualFormats.value(data.iname, -1);
                     if (format == -1)
                         format = m_handler->m_typeFormats.value(data.type, -1);
-                    //qDebug() << "FORMATTED: " << format << formattedValue(data, format);
                     return truncateValue(formattedValue(data, format));
                 }
                 case 2: {
@@ -845,7 +823,7 @@ QVariant WatchModel::data(const QModelIndex &idx, int role) const
             if (isIntType(data.type))
                 return QStringList() << tr("decimal") << tr("hexadecimal")
                     << tr("binary") << tr("octal");
-            if (!data.addr.isEmpty())
+            if (data.type.endsWith(QLatin1Char('*')))
                 return QStringList()
                     << tr("Bald pointer")
                     << tr("Latin1 string")
@@ -889,6 +867,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
         }
     } else if (role == TypeFormatRole) {
         m_handler->setFormat(data.type, value.toInt());
+        m_handler->m_manager->updateWatchData(data);
     } else if (role == IndividualFormatRole) {
         const int format = value.toInt();
         if (format == -1) {
@@ -896,6 +875,7 @@ bool WatchModel::setData(const QModelIndex &index, const QVariant &value, int ro
         } else {
             m_handler->m_individualFormats[data.iname] = format;
         }
+        m_handler->m_manager->updateWatchData(data);
     }
     emit dataChanged(index, index);
     return true;
@@ -1155,8 +1135,8 @@ WatchItem *WatchModel::findItem(const QByteArray &iname, WatchItem *root) const
 static void debugRecursion(QDebug &d, const WatchItem *item, int depth)
 {
     d << QString(2 * depth, QLatin1Char(' ')) << item->toString() << '\n';
-    foreach(const WatchItem *i, item->children)
-        debugRecursion(d, i, depth + 1);
+    foreach (const WatchItem *child, item->children)
+        debugRecursion(d, child, depth + 1);
 }
 
 QDebug operator<<(QDebug d, const WatchModel &m)
@@ -1167,6 +1147,16 @@ QDebug operator<<(QDebug d, const WatchModel &m)
     return d;
 }
 
+void WatchModel::formatRequests(QByteArray *out, const WatchItem *item) const
+{
+    int format = m_handler->m_individualFormats.value(item->iname, -1);
+    if (format == -1)
+        format = m_handler->m_typeFormats.value(item->type, -1);
+    if (format != -1)
+        *out += item->iname + ":format=" + QByteArray::number(format) + ',';
+    foreach (const WatchItem *child, item->children)
+        formatRequests(out, child);
+}
 
 ///////////////////////////////////////////////////////////////////////
 //
@@ -1573,5 +1563,65 @@ void WatchHandler::setFormat(const QString &type, int format)
     m_tooltips->emitDataChanged(1);
 }
 
+int WatchHandler::format(const QByteArray &iname) const
+{
+    int result = -1;
+    if (const WatchData *item = findItem(iname)) {
+        int result = m_individualFormats.value(iname, -1);
+        if (result == -1)
+            result = m_typeFormats.value(item->type, -1);
+    }
+    return result;
+}
+
+QByteArray WatchHandler::formatRequests() const
+{
+    QByteArray ba;
+    //m_locals->formatRequests(&ba, m_locals->m_root);
+    //m_watchers->formatRequests(&ba, m_watchers->m_root);
+
+    ba.append("expanded:");
+    if (!m_expandedINames.isEmpty()) {
+        QSetIterator<QByteArray> jt(m_expandedINames);
+        while (jt.hasNext()) {
+            QByteArray iname = jt.next();
+            ba.append(iname);
+            ba.append(',');
+        }
+        ba.chop(1);
+    }
+    ba.append(' ');
+
+    ba.append("typeformats:");
+    if (!m_typeFormats.isEmpty()) {
+        QHashIterator<QString, int> it(m_typeFormats);
+        while (it.hasNext()) {
+            it.next();
+            ba.append(it.key().toLatin1().toHex());
+            ba.append('=');
+            ba.append(QByteArray::number(it.value()));
+            ba.append(',');
+        }
+        ba.chop(1);
+    }
+    ba.append(' ');
+
+    ba.append("formats:");
+    if (!m_individualFormats.isEmpty()) {
+        QHashIterator<QString, int> it(m_individualFormats);
+        while (it.hasNext()) {
+            it.next();
+            ba.append(it.key().toLatin1());
+            ba.append('=');
+            ba.append(QByteArray::number(it.value()));
+            ba.append(',');
+        }
+        ba.chop(1);
+    }
+    ba.append(' ');
+
+    return ba;
+}
+
 } // namespace Internal
 } // namespace Debugger
diff --git a/src/plugins/debugger/watchhandler.h b/src/plugins/debugger/watchhandler.h
index effec1e7329..365426b850c 100644
--- a/src/plugins/debugger/watchhandler.h
+++ b/src/plugins/debugger/watchhandler.h
@@ -139,8 +139,6 @@ public:
     bool valueEditable;   // value will be editable
     bool error;
 
-private:
-
 public:
     int source;  // Used by some debuggers (CDB) to tell where it originates from (dumper or symbol evaluation)
     int state;
@@ -239,6 +237,7 @@ signals:
 
 private:
     QString niceType(const QString &typeIn) const;
+    void formatRequests(QByteArray *out, const WatchItem *item) const;
 
     WatchHandler *m_handler;
     WatchType m_type;
@@ -284,8 +283,10 @@ public:
     QStringList watchedExpressions() const;
     QHash<QByteArray, int> watcherNames() const
         { return m_watcherNames; }
+    QByteArray formatRequests() const;
 
     static QString watcherEditPlaceHolder();
+    int format(const QByteArray &iname) const;
 
 private:
     friend class WatchModel;
diff --git a/src/plugins/debugger/watchutils.cpp b/src/plugins/debugger/watchutils.cpp
index f84f98835e5..bda7f9d373b 100644
--- a/src/plugins/debugger/watchutils.cpp
+++ b/src/plugins/debugger/watchutils.cpp
@@ -640,7 +640,7 @@ QString decodeData(const QByteArray &ba, int encoding)
         case 5: { //  base64 encoded 8 bit data, without quotes (see 1)
             return quoteUnprintableLatin1(QByteArray::fromBase64(ba));
         }
-        case 6: { //  %02x encoded 8 bit data
+        case 6: { //  %02x encoded 8 bit Latin1 data
             const QChar doubleQuote(QLatin1Char('"'));
             const QByteArray decodedBa = QByteArray::fromHex(ba);
             //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
@@ -660,6 +660,39 @@ QString decodeData(const QByteArray &ba, int encoding)
             return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
                 (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
         }
+        case 9: { //  %02x encoded 8 bit Utf-8 data
+            const QChar doubleQuote(QLatin1Char('"'));
+            const QByteArray decodedBa = QByteArray::fromHex(ba);
+            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
+            return doubleQuote + QString::fromUtf8(decodedBa) + doubleQuote;
+        }
+        case 10: { //  %08x encoded 32 bit data, Big Endian
+            const QChar doubleQuote(QLatin1Char('"'));
+            QByteArray decodedBa = QByteArray::fromHex(ba);
+            for (int i = 0; i < decodedBa.size(); i += 4) {
+                char c = decodedBa.at(i);
+                decodedBa[i] = decodedBa.at(i + 3);
+                decodedBa[i + 3] = c;
+                c = decodedBa.at(i + 1);
+                decodedBa[i + 1] = decodedBa.at(i + 2);
+                decodedBa[i + 2] = c;
+            }
+            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
+            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
+                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
+        }
+        case 11: { //  %02x encoded 16 bit data, Big Endian
+            const QChar doubleQuote(QLatin1Char('"'));
+            QByteArray decodedBa = QByteArray::fromHex(ba);
+            for (int i = 0; i < decodedBa.size(); i += 2) {
+                char c = decodedBa.at(i);
+                decodedBa[i] = decodedBa.at(i + 1);
+                decodedBa[i + 1] = c;
+            }
+            //qDebug() << quoteUnprintableLatin1(decodedBa) << "\n\n";
+            return doubleQuote + QString::fromUcs4(reinterpret_cast<const uint *>
+                (decodedBa.data()), decodedBa.size() / 4) + doubleQuote;
+        }
     }
     qDebug() << "ENCODING ERROR: " << encoding;
     return QCoreApplication::translate("Debugger", "<Encoding error>");
diff --git a/src/plugins/debugger/watchwindow.cpp b/src/plugins/debugger/watchwindow.cpp
index 268c53ba812..7d076e26594 100644
--- a/src/plugins/debugger/watchwindow.cpp
+++ b/src/plugins/debugger/watchwindow.cpp
@@ -269,7 +269,8 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
     actWatchUnknownMemory->setEnabled(actionsEnabled && canShowMemory);
 
     if (canShowMemory && !address.isEmpty())
-        actWatchKnownMemory = new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
+        actWatchKnownMemory =
+            new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
     menu.addSeparator();
 
     QAction *actWatchOrRemove;
@@ -293,8 +294,10 @@ void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
 
     menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
     menu.addAction(theDebuggerAction(UseDebuggingHelpers));
-    QAction *actClearCodeModelSnapshot = new QAction(tr("Refresh Code Model Snapshot"), &menu);
-    actClearCodeModelSnapshot->setEnabled(actionsEnabled && theDebuggerAction(UseCodeModel)->isChecked());
+    QAction *actClearCodeModelSnapshot
+        = new QAction(tr("Refresh Code Model Snapshot"), &menu);
+    actClearCodeModelSnapshot->setEnabled(actionsEnabled
+        && theDebuggerAction(UseCodeModel)->isChecked());
     menu.addAction(actClearCodeModelSnapshot);
     menu.addSeparator();
     menu.addAction(theDebuggerAction(UseToolTipsInLocalsView));
diff --git a/tests/manual/gdbdebugger/simple/app.cpp b/tests/manual/gdbdebugger/simple/app.cpp
index 316ed1ce78a..18540d2cfb3 100644
--- a/tests/manual/gdbdebugger/simple/app.cpp
+++ b/tests/manual/gdbdebugger/simple/app.cpp
@@ -1486,6 +1486,8 @@ int main(int argc, char *argv[])
     testPlugin();
     testQList();
     testQLinkedList();
+	char *s = "aöa";
+	wchar_t *w = L"aöa";
     testNamespace();
     //return 0;
     testQHash();
-- 
GitLab