@@ -102,12 +102,25 @@ void metricgroup__rblist_exit(struct rblist *metric_events)
102102 rblist__exit (metric_events );
103103}
104104
105+ /*
106+ * A node in the list of referenced metrics. metric_expr
107+ * is held as a convenience to avoid a search through the
108+ * metric list.
109+ */
110+ struct metric_ref_node {
111+ const char * metric_name ;
112+ const char * metric_expr ;
113+ struct list_head list ;
114+ };
115+
105116struct egroup {
106117 struct list_head nd ;
107118 struct expr_parse_ctx pctx ;
108119 const char * metric_name ;
109120 const char * metric_expr ;
110121 const char * metric_unit ;
122+ struct list_head metric_refs ;
123+ int metric_refs_cnt ;
111124 int runtime ;
112125 bool has_constraint ;
113126};
@@ -574,27 +587,72 @@ int __weak arch_get_runtimeparam(void)
574587static int __add_metric (struct list_head * group_list ,
575588 struct pmu_event * pe ,
576589 bool metric_no_group ,
577- int runtime )
590+ int runtime ,
591+ struct egroup * * egp )
578592{
593+ struct metric_ref_node * ref ;
579594 struct egroup * eg ;
580595
581- eg = malloc (sizeof (* eg ));
582- if (!eg )
583- return - ENOMEM ;
596+ if (* egp == NULL ) {
597+ /*
598+ * We got in here for the parent group,
599+ * allocate it and put it on the list.
600+ */
601+ eg = malloc (sizeof (* eg ));
602+ if (!eg )
603+ return - ENOMEM ;
604+
605+ expr__ctx_init (& eg -> pctx );
606+ eg -> metric_name = pe -> metric_name ;
607+ eg -> metric_expr = pe -> metric_expr ;
608+ eg -> metric_unit = pe -> unit ;
609+ eg -> runtime = runtime ;
610+ eg -> has_constraint = metric_no_group || metricgroup__has_constraint (pe );
611+ INIT_LIST_HEAD (& eg -> metric_refs );
612+ eg -> metric_refs_cnt = 0 ;
613+ * egp = eg ;
614+ } else {
615+ /*
616+ * We got here for the referenced metric, via the
617+ * recursive metricgroup__add_metric call, add
618+ * it to the parent group.
619+ */
620+ eg = * egp ;
621+
622+ ref = malloc (sizeof (* ref ));
623+ if (!ref )
624+ return - ENOMEM ;
584625
585- expr__ctx_init (& eg -> pctx );
586- eg -> metric_name = pe -> metric_name ;
587- eg -> metric_expr = pe -> metric_expr ;
588- eg -> metric_unit = pe -> unit ;
589- eg -> runtime = runtime ;
590- eg -> has_constraint = metric_no_group || metricgroup__has_constraint (pe );
626+ /*
627+ * Intentionally passing just const char pointers,
628+ * from 'pe' object, so they never go away. We don't
629+ * need to change them, so there's no need to create
630+ * our own copy.
631+ */
632+ ref -> metric_name = pe -> metric_name ;
633+ ref -> metric_expr = pe -> metric_expr ;
634+
635+ list_add (& ref -> list , & eg -> metric_refs );
636+ eg -> metric_refs_cnt ++ ;
637+ }
591638
639+ /*
640+ * For both the parent and referenced metrics, we parse
641+ * all the metric's IDs and add it to the parent context.
642+ */
592643 if (expr__find_other (pe -> metric_expr , NULL , & eg -> pctx , runtime ) < 0 ) {
593644 expr__ctx_clear (& eg -> pctx );
594645 free (eg );
595646 return - EINVAL ;
596647 }
597648
649+ /*
650+ * We add new group only in the 'parent' call,
651+ * so bail out for referenced metric case.
652+ */
653+ if (eg -> metric_refs_cnt )
654+ return 0 ;
655+
598656 if (list_empty (group_list ))
599657 list_add (& eg -> nd , group_list );
600658 else {
@@ -625,16 +683,94 @@ static int __add_metric(struct list_head *group_list,
625683 (match_metric(__pe->metric_group, __metric) || \
626684 match_metric(__pe->metric_name, __metric)))
627685
686+ static struct pmu_event * find_metric (const char * metric , struct pmu_events_map * map )
687+ {
688+ struct pmu_event * pe ;
689+ int i ;
690+
691+ map_for_each_event (pe , i , map ) {
692+ if (match_metric (pe -> metric_name , metric ))
693+ return pe ;
694+ }
695+
696+ return NULL ;
697+ }
698+
628699static int add_metric (struct list_head * group_list ,
629700 struct pmu_event * pe ,
630- bool metric_no_group )
701+ bool metric_no_group ,
702+ struct egroup * * egp );
703+
704+ static int __resolve_metric (struct egroup * eg ,
705+ bool metric_no_group ,
706+ struct list_head * group_list ,
707+ struct pmu_events_map * map )
631708{
709+ struct hashmap_entry * cur ;
710+ size_t bkt ;
711+ bool all ;
712+ int ret ;
713+
714+ /*
715+ * Iterate all the parsed IDs and if there's metric,
716+ * add it to the context.
717+ */
718+ do {
719+ all = true;
720+ hashmap__for_each_entry ((& eg -> pctx .ids ), cur , bkt ) {
721+ struct pmu_event * pe ;
722+
723+ pe = find_metric (cur -> key , map );
724+ if (!pe )
725+ continue ;
726+
727+ all = false;
728+ /* The metric key itself needs to go out.. */
729+ expr__del_id (& eg -> pctx , cur -> key );
730+
731+ /* ... and it gets resolved to the parent context. */
732+ ret = add_metric (group_list , pe , metric_no_group , & eg );
733+ if (ret )
734+ return ret ;
735+
736+ /*
737+ * We added new metric to hashmap, so we need
738+ * to break the iteration and start over.
739+ */
740+ break ;
741+ }
742+ } while (!all );
743+
744+ return 0 ;
745+ }
746+
747+ static int resolve_metric (bool metric_no_group ,
748+ struct list_head * metric_list ,
749+ struct pmu_events_map * map )
750+ {
751+ struct egroup * eg ;
752+ int err ;
753+
754+ list_for_each_entry (eg , metric_list , nd ) {
755+ err = __resolve_metric (eg , metric_no_group , metric_list , map );
756+ if (err )
757+ return err ;
758+ }
759+ return 0 ;
760+ }
761+
762+ static int add_metric (struct list_head * group_list ,
763+ struct pmu_event * pe ,
764+ bool metric_no_group ,
765+ struct egroup * * egp )
766+ {
767+ struct egroup * orig = * egp ;
632768 int ret = 0 ;
633769
634770 pr_debug ("metric expr %s for %s\n" , pe -> metric_expr , pe -> metric_name );
635771
636772 if (!strstr (pe -> metric_expr , "?" )) {
637- ret = __add_metric (group_list , pe , metric_no_group , 1 );
773+ ret = __add_metric (group_list , pe , metric_no_group , 1 , egp );
638774 } else {
639775 int j , count ;
640776
@@ -645,9 +781,8 @@ static int add_metric(struct list_head *group_list,
645781 * those events to group_list.
646782 */
647783
648- for (j = 0 ; j < count && !ret ; j ++ ) {
649- ret = __add_metric (group_list , pe , metric_no_group , j );
650- }
784+ for (j = 0 ; j < count && !ret ; j ++ , * egp = orig )
785+ ret = __add_metric (group_list , pe , metric_no_group , j , egp );
651786 }
652787
653788 return ret ;
@@ -665,8 +800,18 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
665800
666801 map_for_each_metric (pe , i , map , metric ) {
667802 has_match = true;
803+ eg = NULL ;
804+
805+ ret = add_metric (group_list , pe , metric_no_group , & eg );
806+ if (ret )
807+ return ret ;
668808
669- ret = add_metric (group_list , pe , metric_no_group );
809+ /*
810+ * Process any possible referenced metrics
811+ * included in the expression.
812+ */
813+ ret = resolve_metric (metric_no_group ,
814+ group_list , map );
670815 if (ret )
671816 return ret ;
672817 }
@@ -723,11 +868,22 @@ static int metricgroup__add_metric_list(const char *list, bool metric_no_group,
723868 return ret ;
724869}
725870
871+ static void egroup__free_refs (struct egroup * egroup )
872+ {
873+ struct metric_ref_node * ref , * tmp ;
874+
875+ list_for_each_entry_safe (ref , tmp , & egroup -> metric_refs , list ) {
876+ list_del (& ref -> list );
877+ free (ref );
878+ }
879+ }
880+
726881static void metricgroup__free_egroups (struct list_head * group_list )
727882{
728883 struct egroup * eg , * egtmp ;
729884
730885 list_for_each_entry_safe (eg , egtmp , group_list , nd ) {
886+ egroup__free_refs (eg );
731887 expr__ctx_clear (& eg -> pctx );
732888 list_del_init (& eg -> nd );
733889 free (eg );
0 commit comments