|
@@ -867,6 +867,7 @@ static int cs_etm__sample(struct cs_etm_queue *etmq)
|
|
|
*/
|
|
|
if (etm->synth_opts.last_branch &&
|
|
|
etmq->prev_packet &&
|
|
|
+ etmq->prev_packet->sample_type == CS_ETM_RANGE &&
|
|
|
etmq->prev_packet->last_instr_taken_branch)
|
|
|
cs_etm__update_last_branch_rb(etmq);
|
|
|
|
|
@@ -920,6 +921,40 @@ static int cs_etm__sample(struct cs_etm_queue *etmq)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int cs_etm__flush(struct cs_etm_queue *etmq)
|
|
|
+{
|
|
|
+ int err = 0;
|
|
|
+ struct cs_etm_packet *tmp;
|
|
|
+
|
|
|
+ if (etmq->etm->synth_opts.last_branch &&
|
|
|
+ etmq->prev_packet &&
|
|
|
+ etmq->prev_packet->sample_type == CS_ETM_RANGE) {
|
|
|
+ /*
|
|
|
+ * Generate a last branch event for the branches left in the
|
|
|
+ * circular buffer at the end of the trace.
|
|
|
+ *
|
|
|
+ * Use the address of the end of the last reported execution
|
|
|
+ * range
|
|
|
+ */
|
|
|
+ u64 addr = cs_etm__last_executed_instr(etmq->prev_packet);
|
|
|
+
|
|
|
+ err = cs_etm__synth_instruction_sample(
|
|
|
+ etmq, addr,
|
|
|
+ etmq->period_instructions);
|
|
|
+ etmq->period_instructions = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Swap PACKET with PREV_PACKET: PACKET becomes PREV_PACKET for
|
|
|
+ * the next incoming packet.
|
|
|
+ */
|
|
|
+ tmp = etmq->packet;
|
|
|
+ etmq->packet = etmq->prev_packet;
|
|
|
+ etmq->prev_packet = tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
|
|
|
{
|
|
|
struct cs_etm_auxtrace *etm = etmq->etm;
|
|
@@ -971,32 +1006,31 @@ static int cs_etm__run_decoder(struct cs_etm_queue *etmq)
|
|
|
*/
|
|
|
break;
|
|
|
|
|
|
- /*
|
|
|
- * If the packet contains an instruction
|
|
|
- * range, generate instruction sequence
|
|
|
- * events.
|
|
|
- */
|
|
|
- if (etmq->packet->sample_type & CS_ETM_RANGE)
|
|
|
- err = cs_etm__sample(etmq);
|
|
|
+ switch (etmq->packet->sample_type) {
|
|
|
+ case CS_ETM_RANGE:
|
|
|
+ /*
|
|
|
+ * If the packet contains an instruction
|
|
|
+ * range, generate instruction sequence
|
|
|
+ * events.
|
|
|
+ */
|
|
|
+ cs_etm__sample(etmq);
|
|
|
+ break;
|
|
|
+ case CS_ETM_TRACE_ON:
|
|
|
+ /*
|
|
|
+ * Discontinuity in trace, flush
|
|
|
+ * previous branch stack
|
|
|
+ */
|
|
|
+ cs_etm__flush(etmq);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
} while (buffer.len > buffer_used);
|
|
|
|
|
|
- /*
|
|
|
- * Generate a last branch event for the branches left in
|
|
|
- * the circular buffer at the end of the trace.
|
|
|
- */
|
|
|
- if (etm->sample_instructions &&
|
|
|
- etmq->etm->synth_opts.last_branch) {
|
|
|
- struct branch_stack *bs = etmq->last_branch_rb;
|
|
|
- struct branch_entry *be =
|
|
|
- &bs->entries[etmq->last_branch_pos];
|
|
|
-
|
|
|
- err = cs_etm__synth_instruction_sample(
|
|
|
- etmq, be->to, etmq->period_instructions);
|
|
|
- if (err)
|
|
|
- return err;
|
|
|
- }
|
|
|
-
|
|
|
+ if (err == 0)
|
|
|
+ /* Flush any remaining branch stack entries */
|
|
|
+ err = cs_etm__flush(etmq);
|
|
|
}
|
|
|
|
|
|
return err;
|