On Sat, Jan 19, 2019 at 09:43:46AM +0800, Leo Yan wrote:
> The exception taken and returning are typical flow for instruction jump
> but it needs to be handled with exception packets. This patch is to set
> sample flags for exception packet.
> 
> Since the exception packet contains the exception number, according to
> the exception number this patch makes decision for belonging to which
> exception types.
> 
> The decoder have defined different exception number for ETMv3 and ETMv4
> separately, hence this patch needs firstly decide the ETM version by
> using the metadata magic number, and this patch adds helper function
> cs_etm__get_magic() for easily getting magic number.
> 
> Based on different ETM version, the exception packet contains the
> exception number, according to the exception number this patch makes
> decision for the exception belonging to which exception types.
> 
> In this patch, it introduces helper function cs_etm__is_svc_instr();
> for ETMv4 CS_ETMV4_EXC_CALL covers SVC, SMC and HVC cases in the
> single exception number, thus need to use cs_etm__is_svc_instr() to
> decide an exception taken for system call.
> 
> Reviewed-by: Robert Walker <[email protected]>
> Signed-off-by: Leo Yan <[email protected]>

This is the other way around, i.e you wrote the code and then Robert reviewed
it.  With this change:

Reviewed-by: Mathieu Poirier <[email protected]>

> ---
>  tools/perf/util/cs-etm.c | 215 +++++++++++++++++++++++++++++++++++++++
>  tools/perf/util/cs-etm.h |  44 ++++++++
>  2 files changed, 259 insertions(+)
> 
> diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c
> index e89989fe0a5c..052805de6513 100644
> --- a/tools/perf/util/cs-etm.c
> +++ b/tools/perf/util/cs-etm.c
> @@ -97,6 +97,20 @@ static u32 cs_etm__get_v7_protocol_version(u32 etmidr)
>       return CS_ETM_PROTO_ETMV3;
>  }
>  
> +static int cs_etm__get_magic(u8 trace_chan_id, u64 *magic)
> +{
> +     struct int_node *inode;
> +     u64 *metadata;
> +
> +     inode = intlist__find(traceid_list, trace_chan_id);
> +     if (!inode)
> +             return -EINVAL;
> +
> +     metadata = inode->priv;
> +     *magic = metadata[CS_ETM_MAGIC];
> +     return 0;
> +}
> +
>  int cs_etm__get_cpu(u8 trace_chan_id, int *cpu)
>  {
>       struct int_node *inode;
> @@ -1122,10 +1136,174 @@ static int cs_etm__end_block(struct cs_etm_queue 
> *etmq)
>       return 0;
>  }
>  
> +static bool cs_etm__is_svc_instr(struct cs_etm_queue *etmq,
> +                              struct cs_etm_packet *packet,
> +                              u64 end_addr)
> +{
> +     u16 instr16;
> +     u32 instr32;
> +     u64 addr;
> +
> +     switch (packet->isa) {
> +     case CS_ETM_ISA_T32:
> +             /*
> +              * The SVC of T32 is defined in ARM DDI 0487D.a, F5.1.247:
> +              *
> +              *  b'15         b'8
> +              * +-----------------+--------+
> +              * | 1 1 0 1 1 1 1 1 |  imm8  |
> +              * +-----------------+--------+
> +              *
> +              * According to the specifiction, it only defines SVC for T32
> +              * with 16 bits instruction and has no definition for 32bits;
> +              * so below only read 2 bytes as instruction size for T32.
> +              */
> +             addr = end_addr - 2;
> +             cs_etm__mem_access(etmq, addr, sizeof(instr16), (u8 *)&instr16);
> +             if ((instr16 & 0xFF00) == 0xDF00)
> +                     return true;
> +
> +             break;
> +     case CS_ETM_ISA_A32:
> +             /*
> +              * The SVC of A32 is defined in ARM DDI 0487D.a, F5.1.247:
> +              *
> +              *  b'31 b'28 b'27 b'24
> +              * +---------+---------+-------------------------+
> +              * |  !1111  | 1 1 1 1 |        imm24            |
> +              * +---------+---------+-------------------------+
> +              */
> +             addr = end_addr - 4;
> +             cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
> +             if ((instr32 & 0x0F000000) == 0x0F000000 &&
> +                 (instr32 & 0xF0000000) != 0xF0000000)
> +                     return true;
> +
> +             break;
> +     case CS_ETM_ISA_A64:
> +             /*
> +              * The SVC of A64 is defined in ARM DDI 0487D.a, C6.2.294:
> +              *
> +              *  b'31               b'21           b'4     b'0
> +              * +-----------------------+---------+-----------+
> +              * | 1 1 0 1 0 1 0 0 0 0 0 |  imm16  | 0 0 0 0 1 |
> +              * +-----------------------+---------+-----------+
> +              */
> +             addr = end_addr - 4;
> +             cs_etm__mem_access(etmq, addr, sizeof(instr32), (u8 *)&instr32);
> +             if ((instr32 & 0xFFE0001F) == 0xd4000001)
> +                     return true;
> +
> +             break;
> +     case CS_ETM_ISA_UNKNOWN:
> +     default:
> +             break;
> +     }
> +
> +     return false;
> +}
> +
> +static bool cs_etm__is_syscall(struct cs_etm_queue *etmq, u64 magic)
> +{
> +     struct cs_etm_packet *packet = etmq->packet;
> +     struct cs_etm_packet *prev_packet = etmq->prev_packet;
> +
> +     if (magic == __perf_cs_etmv3_magic)
> +             if (packet->exception_number == CS_ETMV3_EXC_SVC)
> +                     return true;
> +
> +     /*
> +      * ETMv4 exception type CS_ETMV4_EXC_CALL covers SVC, SMC and
> +      * HVC cases; need to check if it's SVC instruction based on
> +      * packet address.
> +      */
> +     if (magic == __perf_cs_etmv4_magic) {
> +             if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> +                 cs_etm__is_svc_instr(etmq, prev_packet,
> +                                      prev_packet->end_addr))
> +                     return true;
> +     }
> +
> +     return false;
> +}
> +
> +static bool cs_etm__is_async_exception(struct cs_etm_queue *etmq, u64 magic)
> +{
> +     struct cs_etm_packet *packet = etmq->packet;
> +
> +     if (magic == __perf_cs_etmv3_magic)
> +             if (packet->exception_number == CS_ETMV3_EXC_DEBUG_HALT ||
> +                 packet->exception_number == CS_ETMV3_EXC_ASYNC_DATA_ABORT ||
> +                 packet->exception_number == CS_ETMV3_EXC_PE_RESET ||
> +                 packet->exception_number == CS_ETMV3_EXC_IRQ ||
> +                 packet->exception_number == CS_ETMV3_EXC_FIQ)
> +                     return true;
> +
> +     if (magic == __perf_cs_etmv4_magic)
> +             if (packet->exception_number == CS_ETMV4_EXC_RESET ||
> +                 packet->exception_number == CS_ETMV4_EXC_DEBUG_HALT ||
> +                 packet->exception_number == CS_ETMV4_EXC_SYSTEM_ERROR ||
> +                 packet->exception_number == CS_ETMV4_EXC_INST_DEBUG ||
> +                 packet->exception_number == CS_ETMV4_EXC_DATA_DEBUG ||
> +                 packet->exception_number == CS_ETMV4_EXC_IRQ ||
> +                 packet->exception_number == CS_ETMV4_EXC_FIQ)
> +                     return true;
> +
> +     return false;
> +}
> +
> +static bool cs_etm__is_sync_exception(struct cs_etm_queue *etmq, u64 magic)
> +{
> +     struct cs_etm_packet *packet = etmq->packet;
> +     struct cs_etm_packet *prev_packet = etmq->prev_packet;
> +
> +     if (magic == __perf_cs_etmv3_magic)
> +             if (packet->exception_number == CS_ETMV3_EXC_SMC ||
> +                 packet->exception_number == CS_ETMV3_EXC_HYP ||
> +                 packet->exception_number == CS_ETMV3_EXC_JAZELLE_THUMBEE ||
> +                 packet->exception_number == CS_ETMV3_EXC_UNDEFINED_INSTR ||
> +                 packet->exception_number == CS_ETMV3_EXC_PREFETCH_ABORT ||
> +                 packet->exception_number == CS_ETMV3_EXC_DATA_FAULT ||
> +                 packet->exception_number == CS_ETMV3_EXC_GENERIC)
> +                     return true;
> +
> +     if (magic == __perf_cs_etmv4_magic) {
> +             if (packet->exception_number == CS_ETMV4_EXC_TRAP ||
> +                 packet->exception_number == CS_ETMV4_EXC_ALIGNMENT ||
> +                 packet->exception_number == CS_ETMV4_EXC_INST_FAULT ||
> +                 packet->exception_number == CS_ETMV4_EXC_DATA_FAULT)
> +                     return true;
> +
> +             /*
> +              * For CS_ETMV4_EXC_CALL, except SVC other instructions
> +              * (SMC, HVC) are taken as sync exceptions.
> +              */
> +             if (packet->exception_number == CS_ETMV4_EXC_CALL &&
> +                 !cs_etm__is_svc_instr(etmq, prev_packet,
> +                                       prev_packet->end_addr))
> +                     return true;
> +
> +             /*
> +              * ETMv4 has 5 bits for exception number; if the numbers
> +              * are in the range ( CS_ETMV4_EXC_FIQ, CS_ETMV4_EXC_END ]
> +              * they are implementation defined exceptions.
> +              *
> +              * For this case, simply take it as sync exception.
> +              */
> +             if (packet->exception_number > CS_ETMV4_EXC_FIQ &&
> +                 packet->exception_number <= CS_ETMV4_EXC_END)
> +                     return true;
> +     }
> +
> +     return false;
> +}
> +
>  static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq)
>  {
>       struct cs_etm_packet *packet = etmq->packet;
>       struct cs_etm_packet *prev_packet = etmq->prev_packet;
> +     u64 magic;
> +     int ret;
>  
>       switch (packet->sample_type) {
>       case CS_ETM_RANGE:
> @@ -1206,6 +1384,43 @@ static int cs_etm__set_sample_flags(struct 
> cs_etm_queue *etmq)
>                                             PERF_IP_FLAG_TRACE_END;
>               break;
>       case CS_ETM_EXCEPTION:
> +             ret = cs_etm__get_magic(packet->trace_chan_id, &magic);
> +             if (ret)
> +                     return ret;
> +
> +             /* The exception is for system call. */
> +             if (cs_etm__is_syscall(etmq, magic))
> +                     packet->flags = PERF_IP_FLAG_BRANCH |
> +                                     PERF_IP_FLAG_CALL |
> +                                     PERF_IP_FLAG_SYSCALLRET;
> +             /*
> +              * The exceptions are triggered by external signals from bus,
> +              * interrupt controller, debug module, PE reset or halt.
> +              */
> +             else if (cs_etm__is_async_exception(etmq, magic))
> +                     packet->flags = PERF_IP_FLAG_BRANCH |
> +                                     PERF_IP_FLAG_CALL |
> +                                     PERF_IP_FLAG_ASYNC |
> +                                     PERF_IP_FLAG_INTERRUPT;
> +             /*
> +              * Otherwise, exception is caused by trap, instruction &
> +              * data fault, or alignment errors.
> +              */
> +             else if (cs_etm__is_sync_exception(etmq, magic))
> +                     packet->flags = PERF_IP_FLAG_BRANCH |
> +                                     PERF_IP_FLAG_CALL |
> +                                     PERF_IP_FLAG_INTERRUPT;
> +
> +             /*
> +              * When the exception packet is inserted, since exception
> +              * packet is not used standalone for generating samples
> +              * and it's affiliation to the previous instruction range
> +              * packet; so set previous range packet flags to tell perf
> +              * it is an exception taken branch.
> +              */
> +             if (prev_packet->sample_type == CS_ETM_RANGE)
> +                     prev_packet->flags = packet->flags;
> +             break;
>       case CS_ETM_EXCEPTION_RET:
>       case CS_ETM_EMPTY:
>       default:
> diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h
> index 5d70d10f3907..7bd16ea8a62d 100644
> --- a/tools/perf/util/cs-etm.h
> +++ b/tools/perf/util/cs-etm.h
> @@ -53,6 +53,50 @@ enum {
>       CS_ETMV4_PRIV_MAX,
>  };
>  
> +/*
> + * ETMv3 exception encoding number:
> + * See Embedded Trace Macrocell spcification (ARM IHI 0014Q)
> + * table 7-12 Encoding of Exception[3:0] for non-ARMv7-M processors.
> + */
> +enum {
> +     CS_ETMV3_EXC_NONE = 0,
> +     CS_ETMV3_EXC_DEBUG_HALT = 1,
> +     CS_ETMV3_EXC_SMC = 2,
> +     CS_ETMV3_EXC_HYP = 3,
> +     CS_ETMV3_EXC_ASYNC_DATA_ABORT = 4,
> +     CS_ETMV3_EXC_JAZELLE_THUMBEE = 5,
> +     CS_ETMV3_EXC_PE_RESET = 8,
> +     CS_ETMV3_EXC_UNDEFINED_INSTR = 9,
> +     CS_ETMV3_EXC_SVC = 10,
> +     CS_ETMV3_EXC_PREFETCH_ABORT = 11,
> +     CS_ETMV3_EXC_DATA_FAULT = 12,
> +     CS_ETMV3_EXC_GENERIC = 13,
> +     CS_ETMV3_EXC_IRQ = 14,
> +     CS_ETMV3_EXC_FIQ = 15,
> +};
> +
> +/*
> + * ETMv4 exception encoding number:
> + * See ARM Embedded Trace Macrocell Architecture Specification (ARM IHI 
> 0064D)
> + * table 6-12 Possible values for the TYPE field in an Exception instruction
> + * trace packet, for ARMv7-A/R and ARMv8-A/R PEs.
> + */
> +enum {
> +     CS_ETMV4_EXC_RESET = 0,
> +     CS_ETMV4_EXC_DEBUG_HALT = 1,
> +     CS_ETMV4_EXC_CALL = 2,
> +     CS_ETMV4_EXC_TRAP = 3,
> +     CS_ETMV4_EXC_SYSTEM_ERROR = 4,
> +     CS_ETMV4_EXC_INST_DEBUG = 6,
> +     CS_ETMV4_EXC_DATA_DEBUG = 7,
> +     CS_ETMV4_EXC_ALIGNMENT = 10,
> +     CS_ETMV4_EXC_INST_FAULT = 11,
> +     CS_ETMV4_EXC_DATA_FAULT = 12,
> +     CS_ETMV4_EXC_IRQ = 14,
> +     CS_ETMV4_EXC_FIQ = 15,
> +     CS_ETMV4_EXC_END = 31,
> +};
> +
>  /* RB tree for quick conversion between traceID and metadata pointers */
>  struct intlist *traceid_list;
>  
> -- 
> 2.17.1
> 

Reply via email to