#include <mdb/mdb_modapi.h>
#include <mdb/mdb_demangle.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>
#include <strings.h>
#include <unistd.h>
#include <dlfcn.h>
#include <link.h>
static void *
mdb_dem_alloc(size_t len)
{
return (mdb_alloc(len, UM_SLEEP));
}
static void
mdb_dem_free(void *p, size_t len)
{
mdb_free(p, len);
}
static sysdem_ops_t mdb_dem_demops = {
.alloc = mdb_dem_alloc,
.free = mdb_dem_free
};
mdb_demangler_t *
mdb_dem_load(void)
{
mdb_demangler_t *dmp;
dmp = mdb_alloc(sizeof (mdb_demangler_t), UM_SLEEP);
dmp->dm_len = 0;
dmp->dm_buf = NULL;
dmp->dm_flags = MDB_DM_SCOPE;
dmp->dm_lang = SYSDEM_LANG_AUTO;
return (dmp);
}
void
mdb_dem_unload(mdb_demangler_t *dmp)
{
mdb_free(dmp->dm_buf, dmp->dm_len);
mdb_free(dmp, sizeof (mdb_demangler_t));
}
static const char *
mdb_dem_filter(mdb_demangler_t *dmp, const char *name)
{
static const char s_pref[] = "static ";
static const char c_suff[] = " const";
static const char v_suff[] = " volatile";
size_t len = strlen(dmp->dm_dem);
char *end = dmp->dm_dem + len;
size_t resid;
if (!(dmp->dm_flags & MDB_DM_QUAL)) {
if (strncmp(dmp->dm_dem, s_pref, sizeof (s_pref) - 1) == 0) {
bcopy(dmp->dm_dem + sizeof (s_pref) - 1, dmp->dm_dem,
len - (sizeof (s_pref) - 1) + 1);
end -= sizeof (s_pref) - 1;
len -= sizeof (s_pref) - 1;
}
for (;;) {
if (len > sizeof (c_suff) - 1 &&
strcmp(end - (sizeof (c_suff) - 1), c_suff) == 0) {
end -= sizeof (c_suff) - 1;
len -= sizeof (c_suff) - 1;
*end = '\0';
continue;
}
if (len > sizeof (v_suff) - 1 &&
strcmp(end - (sizeof (v_suff) - 1), v_suff) == 0) {
end -= sizeof (v_suff) - 1;
len -= sizeof (v_suff) - 1;
*end = '\0';
continue;
}
break;
}
}
if (!(dmp->dm_flags & MDB_DM_FUNCARG)) {
char *lp = strchr(dmp->dm_dem, '(');
char *rp = strrchr(dmp->dm_dem, ')');
if (lp != NULL && rp != NULL)
bcopy(rp + 1, lp, strlen(rp) + 1);
}
if (!(dmp->dm_flags & MDB_DM_SCOPE)) {
char *c, *s, *lp = strchr(dmp->dm_dem, '(');
if (lp != NULL)
*lp = '\0';
c = strrchr(dmp->dm_dem, ':');
s = strchr(dmp->dm_dem, ' ');
if (lp != NULL)
*lp = '(';
if (c != NULL) {
if (s == NULL || s > c)
bcopy(c + 1, dmp->dm_dem, strlen(c + 1) + 1);
else
bcopy(c + 1, s + 1, strlen(c + 1) + 1);
}
}
len = strlen(dmp->dm_dem);
resid = (dmp->dm_buf + dmp->dm_len) - (dmp->dm_dem + len);
if ((dmp->dm_flags & MDB_DM_MANGLED) && resid > 3) {
char *p = dmp->dm_dem + len;
*p++ = '[';
(void) strncpy(p, name, resid - 3);
p[resid - 3] = '\0';
p += strlen(p);
(void) strcpy(p, "]");
}
return (dmp->dm_buf);
}
static int
mdb_dem_process(mdb_demangler_t *dmp, const char *name)
{
char *res = NULL;
size_t reslen = 0;
char *prefix = strrchr(name, '`');
size_t prefixlen = 0;
if (prefix) {
prefix++;
prefixlen = prefix - name;
}
res = sysdemangle(name + prefixlen, dmp->dm_lang, &mdb_dem_demops);
if (res == NULL) {
if (errno != EINVAL && errno != ENOTSUP)
mdb_warn("Error while demangling");
return (-1);
}
reslen = (res != NULL) ? strlen(res) : 0;
reslen += prefixlen;
reslen += 1;
if (reslen > dmp->dm_len) {
mdb_free(dmp->dm_buf, dmp->dm_len);
dmp->dm_buf = mdb_zalloc(reslen, UM_SLEEP);
if (dmp->dm_buf == NULL) {
dmp->dm_len = 0;
mdb_warn("Unable to allocate memory for demangling");
return (-1);
}
dmp->dm_len = reslen;
}
if (prefixlen > 0)
(void) strlcpy(dmp->dm_buf, name, prefixlen + 1);
else
*dmp->dm_buf = '\0';
if (res != NULL) {
(void) strlcat(dmp->dm_buf, res, dmp->dm_len);
mdb_dem_free(res, strlen(res) + 1);
}
dmp->dm_dem = dmp->dm_buf + prefixlen;
return (0);
}
const char *
mdb_dem_convert(mdb_demangler_t *dmp, const char *name)
{
if (mdb_dem_process(dmp, name) != 0 ||
strcmp(dmp->dm_buf, name) == 0)
return (name);
return (mdb_dem_filter(dmp, name));
}
int
cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
mdb_demangler_t *dmp = mdb.m_demangler;
if (argc > 0)
return (DCMD_USAGE);
if (dmp != NULL && !(mdb.m_flags & MDB_FL_DEMANGLE)) {
mdb_printf("C++ symbol demangling enabled\n");
mdb.m_flags |= MDB_FL_DEMANGLE;
} else if (dmp == NULL) {
if ((mdb.m_demangler = mdb_dem_load()) != NULL) {
mdb_printf("C++ symbol demangling enabled\n");
mdb.m_flags |= MDB_FL_DEMANGLE;
} else {
mdb_warn("no memory to load C++ demangler");
mdb.m_flags &= ~MDB_FL_DEMANGLE;
}
} else {
mdb_dem_unload(mdb.m_demangler);
mdb.m_flags &= ~MDB_FL_DEMANGLE;
mdb.m_demangler = NULL;
mdb_printf("C++ symbol demangling disabled\n");
}
return (DCMD_OK);
}
int
cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
static const char *const dm_desc[] = {
"static/const/volatile member func qualifiers displayed",
"scope resolution specifiers displayed",
"function arguments displayed",
"mangled name displayed"
};
mdb_demangler_t *dmp = mdb.m_demangler;
int i;
if (argc > 0)
return (DCMD_USAGE);
if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) {
mdb_warn("C++ demangling facility is currently disabled\n");
return (DCMD_ERR);
}
if (flags & DCMD_ADDRSPEC)
dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL);
for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) {
mdb_printf("0x%x\t%s\t%s\n", 1 << i,
(dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]);
}
return (DCMD_OK);
}
int
cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
if ((flags & DCMD_ADDRSPEC) || argc == 0)
return (DCMD_USAGE);
if (mdb.m_demangler == NULL && (mdb.m_demangler =
mdb_dem_load()) == NULL) {
mdb_warn("failed to load demangler");
return (DCMD_ERR);
}
for (; argc != 0; argc--, argv++) {
mdb_printf("%s == %s\n", argv->a_un.a_str,
mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str));
}
return (DCMD_OK);
}