Skip to content

Commit d6009e7

Browse files
ptxed: fix error IP reporting
When the instruction flow decoder fails with an error but is still able to provide an instruction, ptxed uses the instruction's IP for reporting the error. For a similar situation with the block decoder, ptxed tries to determine the next instruction's IP for reporting the error. Either approach is more useful for some errors and less useful for others. Use the error code to determine what IP to report and align the reporting. Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
1 parent 89f50ed commit d6009e7

File tree

1 file changed

+108
-61
lines changed

1 file changed

+108
-61
lines changed

ptxed/src/ptxed.c

Lines changed: 108 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,93 @@ static void diagnose(struct ptxed_decoder *decoder, uint64_t ip,
13521352
ip, errtype, pt_errstr(pt_errcode(errcode)));
13531353
}
13541354

1355+
static int xed_next_ip(uint64_t *pip, const xed_decoded_inst_t *inst,
1356+
uint64_t ip)
1357+
{
1358+
xed_uint_t length, disp_width;
1359+
1360+
if (!pip || !inst)
1361+
return -pte_internal;
1362+
1363+
length = xed_decoded_inst_get_length(inst);
1364+
if (!length) {
1365+
printf("[xed error: failed to determine instruction length]\n");
1366+
return -pte_bad_insn;
1367+
}
1368+
1369+
ip += length;
1370+
1371+
/* If it got a branch displacement it must be a branch.
1372+
*
1373+
* This includes conditional branches for which we don't know whether
1374+
* they were taken. The next IP won't be used in this case as a
1375+
* conditional branch ends a block. The next block will start with the
1376+
* correct IP.
1377+
*/
1378+
disp_width = xed_decoded_inst_get_branch_displacement_width(inst);
1379+
if (disp_width)
1380+
ip += (uint64_t) (int64_t)
1381+
xed_decoded_inst_get_branch_displacement(inst);
1382+
1383+
*pip = ip;
1384+
return 0;
1385+
}
1386+
1387+
static void diagnose_insn(struct ptxed_decoder *decoder,
1388+
const char *errtype, int errcode,
1389+
const struct pt_insn *insn)
1390+
{
1391+
xed_decoded_inst_t inst;
1392+
xed_error_enum_t xederr;
1393+
uint64_t ip;
1394+
1395+
if (!decoder || !insn) {
1396+
printf("ptxed: internal error");
1397+
return;
1398+
}
1399+
1400+
/* Determine the IP at which to report the error.
1401+
*
1402+
* When an error is reported by pt_insn_next(), the error may refer to
1403+
* that instruction, e.g. it cannot be decoded, or it may refer to the
1404+
* next instruction, e.g. cannot determine the next IP.
1405+
*
1406+
* As a general rule, if the instruction can be decoded and we can
1407+
* determine the next IP, we attribute the error to the next IP.
1408+
* Otherwise, we attribute it to the instruction's IP.
1409+
*
1410+
* There are exceptions, though, to make the IP we report more useful:
1411+
*
1412+
* -pte_bad_query applies to that instruction.
1413+
*/
1414+
ip = insn->ip;
1415+
switch (errcode) {
1416+
case -pte_bad_query:
1417+
break;
1418+
1419+
default:
1420+
#if (LIBIPT_VERSION >= 0x201)
1421+
if (insn->iclass == ptic_unknown)
1422+
break;
1423+
#else
1424+
if (insn.iclass == ptic_error)
1425+
break;
1426+
#endif
1427+
xed_decoded_inst_zero(&inst);
1428+
xed_decoded_inst_set_mode(&inst,
1429+
translate_mode(insn->mode),
1430+
XED_ADDRESS_WIDTH_INVALID);
1431+
1432+
xederr = xed_decode(&inst, insn->raw, insn->size);
1433+
if (xederr == XED_ERROR_NONE)
1434+
(void) xed_next_ip(&ip, &inst, ip);
1435+
1436+
break;
1437+
}
1438+
1439+
diagnose(decoder, ip, errtype, errcode);
1440+
}
1441+
13551442
#if defined(FEATURE_SIDEBAND)
13561443

13571444
static int ptxed_sb_event(struct ptxed_decoder *decoder,
@@ -1450,8 +1537,8 @@ static void decode_insn(struct ptxed_decoder *decoder,
14501537
struct pt_insn insn;
14511538
int status;
14521539

1453-
/* Initialize the IP - we use it for error reporting. */
1454-
insn.ip = 0ull;
1540+
/* Initialize @insn. We use it for error reporting. */
1541+
memset(&insn, 0, sizeof(insn));
14551542

14561543
status = pt_insn_sync_forward(ptdec);
14571544
if (status < 0) {
@@ -1541,42 +1628,10 @@ static void decode_insn(struct ptxed_decoder *decoder,
15411628
if (status == -pte_eos)
15421629
break;
15431630

1544-
diagnose(decoder, insn.ip, "error", status);
1631+
diagnose_insn(decoder, "error", status, &insn);
15451632
}
15461633
}
15471634

1548-
static int xed_next_ip(uint64_t *pip, const xed_decoded_inst_t *inst,
1549-
uint64_t ip)
1550-
{
1551-
xed_uint_t length, disp_width;
1552-
1553-
if (!pip || !inst)
1554-
return -pte_internal;
1555-
1556-
length = xed_decoded_inst_get_length(inst);
1557-
if (!length) {
1558-
printf("[xed error: failed to determine instruction length]\n");
1559-
return -pte_bad_insn;
1560-
}
1561-
1562-
ip += length;
1563-
1564-
/* If it got a branch displacement it must be a branch.
1565-
*
1566-
* This includes conditional branches for which we don't know whether
1567-
* they were taken. The next IP won't be used in this case as a
1568-
* conditional branch ends a block. The next block will start with the
1569-
* correct IP.
1570-
*/
1571-
disp_width = xed_decoded_inst_get_branch_displacement_width(inst);
1572-
if (disp_width)
1573-
ip += (uint64_t) (int64_t)
1574-
xed_decoded_inst_get_branch_displacement(inst);
1575-
1576-
*pip = ip;
1577-
return 0;
1578-
}
1579-
15801635
static int block_fetch_insn(struct pt_insn *insn, const struct pt_block *block,
15811636
uint64_t ip, struct pt_image_section_cache *iscache)
15821637
{
@@ -1617,6 +1672,9 @@ static void diagnose_block(struct ptxed_decoder *decoder,
16171672
const char *errtype, int errcode,
16181673
const struct pt_block *block)
16191674
{
1675+
struct pt_insn insn;
1676+
xed_decoded_inst_t inst;
1677+
xed_error_enum_t xederr;
16201678
uint64_t ip;
16211679
int err;
16221680

@@ -1627,33 +1685,27 @@ static void diagnose_block(struct ptxed_decoder *decoder,
16271685

16281686
/* Determine the IP at which to report the error.
16291687
*
1630-
* Depending on the type of error, the IP varies between that of the
1631-
* last instruction in @block or the next instruction outside of @block.
1688+
* When an error is reported by pt_blk_next(), the error typically
1689+
* refers to the first instruction after the block.
1690+
*
1691+
* As a general rule, if the last instruction in the block can be
1692+
* decoded and we can determine the next IP, we attribute the error to
1693+
* that IP. Otherwise, we attribute it to the instruction's IP.
16321694
*
1633-
* When the block is empty, we use the IP of the block itself,
1634-
* i.e. where the first instruction should have been.
1695+
* There are exceptions, though, to make the IP we report more useful:
1696+
*
1697+
* -pte_bad_query applies to the last instruction.
16351698
*/
16361699
if (!block->ninsn)
16371700
ip = block->ip;
16381701
else {
16391702
ip = block->end_ip;
16401703

16411704
switch (errcode) {
1642-
case -pte_nomap:
1643-
case -pte_bad_insn: {
1644-
struct pt_insn insn;
1645-
xed_decoded_inst_t inst;
1646-
xed_error_enum_t xederr;
1647-
1648-
/* Decode failed when trying to fetch or decode the next
1649-
* instruction. Since indirect or conditional branches
1650-
* end a block and don't cause an additional fetch, we
1651-
* should be able to reach that IP from the last
1652-
* instruction in @block.
1653-
*
1654-
* We ignore errors and fall back to the IP of the last
1655-
* instruction.
1656-
*/
1705+
case -pte_bad_query:
1706+
break;
1707+
1708+
default:
16571709
err = block_fetch_insn(&insn, block, ip,
16581710
decoder->iscache);
16591711
if (err < 0)
@@ -1665,14 +1717,9 @@ static void diagnose_block(struct ptxed_decoder *decoder,
16651717
XED_ADDRESS_WIDTH_INVALID);
16661718

16671719
xederr = xed_decode(&inst, insn.raw, insn.size);
1668-
if (xederr != XED_ERROR_NONE)
1669-
break;
1670-
1671-
(void) xed_next_ip(&ip, &inst, insn.ip);
1672-
}
1673-
break;
1720+
if (xederr == XED_ERROR_NONE)
1721+
(void) xed_next_ip(&ip, &inst, insn.ip);
16741722

1675-
default:
16761723
break;
16771724
}
16781725
}

0 commit comments

Comments
 (0)