@@ -528,6 +528,35 @@ static int has_expr_breakpoint = FALSE;
528528#ifdef FEAT_PROFILE
529529// Profiling uses file and func names similar to breakpoints.
530530static garray_T prof_ga = {0 , 0 , sizeof (struct debuggy ), 4 , NULL };
531+
532+ // Profiling caches results of regexp lookups for function/script name.
533+ #define N_PROF_HTAB 2
534+ static hashtab_T prof_cache [N_PROF_HTAB ];
535+ #define PROF_HTAB_FUNCS 0
536+ #define PROF_HTAB_FILES 1
537+ static int prof_cache_initialized ;
538+ typedef struct profentry_S
539+ {
540+ char pen_flags ; // cache data booleans: profiling, forceit
541+ char_u pen_name [1 ]; // actually longer
542+ } profentry_T ;
543+ #define PEN_FLAG_PROFILING 1
544+ #define PEN_FLAG_FORCEIT 2
545+ #define PEN_SET_PROFILING (pe ) ((pe)->pen_flags |= PEN_FLAG_PROFILING)
546+ #define PEN_SET_FORCEIT (pe ) ((pe)->pen_flags |= PEN_FLAG_FORCEIT)
547+ #define PEN_IS_PROFILING (pe ) (((pe)->pen_flags & PEN_FLAG_PROFILING) != 0)
548+ #define PEN_IS_FORCEIT (pe ) (((pe)->pen_flags & PEN_FLAG_FORCEIT) != 0)
549+
550+ #define PE2HIKEY (pe ) ((pe)->pen_name)
551+ #define HIKEY2PE (p ) ((profentry_T *)((p) - (offsetof(profentry_T, pen_name))))
552+ #define HI2PE (hi ) HIKEY2PE((hi)->hi_key)
553+
554+ static void prof_clear_cache (void );
555+ #define PROF_CLEAR_CACHE (gap ) do {if ((gap) == &prof_ga) prof_clear_cache();} while (0)
556+ // Can enable to get some info about profile caching
557+ // #define PROF_CACHE_LOG
558+ #else
559+ #define PROF_CLEAR_CACHE (gap ) do {} while (0)
531560#endif
532561#define DBG_FUNC 1
533562#define DBG_FILE 2
@@ -708,6 +737,7 @@ ex_breakadd(exarg_T *eap)
708737 ++ debug_tick ;
709738 }
710739 ++ gap -> ga_len ;
740+ PROF_CLEAR_CACHE (gap );
711741 }
712742 }
713743 else
@@ -845,6 +875,7 @@ ex_breakdel(exarg_T *eap)
845875 if (!del_all )
846876 break ;
847877 }
878+ PROF_CLEAR_CACHE (gap );
848879
849880 // If all breakpoints were removed clear the array.
850881 if (gap -> ga_len == 0 )
@@ -899,17 +930,102 @@ dbg_find_breakpoint(
899930}
900931
901932#if defined(FEAT_PROFILE ) || defined(PROTO )
933+ #if defined(PROF_CACHE_LOG )
934+ static int count_lookups [2 ];
935+ #endif
902936/*
903937 * Return TRUE if profiling is on for a function or sourced file.
938+ * Cache the results of debuggy_find().
939+ * Cache is cleared whenever prof_ga.ga_len is changed.
904940 */
905941 int
906942has_profiling (
907943 int file , // TRUE for a file, FALSE for a function
908944 char_u * fname , // file or function name
909- int * fp ) // return: forceit
945+ int * fp , // return: forceit
946+ hash_T * hashp ) // use/return fname hash, may be NULL
910947{
911- return (debuggy_find (file , fname , (linenr_T )0 , & prof_ga , fp )
912- != (linenr_T )0 );
948+ if (prof_ga .ga_len == 0 || !prof_cache_initialized )
949+ return debuggy_find (file , fname , (linenr_T )0 , & prof_ga , fp )
950+ != (linenr_T )0 ;
951+
952+ hash_T hash ;
953+ if (hashp != NULL )
954+ {
955+ hash = * hashp ;
956+ if (hash == 0 )
957+ {
958+ hash = hash_hash (fname );
959+ * hashp = hash ;
960+ }
961+ }
962+ else
963+ hash = hash_hash (fname );
964+
965+ hashtab_T * ht = & prof_cache [file ? PROF_HTAB_FILES : PROF_HTAB_FUNCS ];
966+ hashitem_T * hi = hash_lookup (ht , fname , hash );
967+ profentry_T * pe ;
968+ if (HASHITEM_EMPTY (hi ))
969+ {
970+ pe = alloc (offsetof(profentry_T , pen_name ) + STRLEN (fname ) + 1 );
971+ if (pe == NULL )
972+ return FALSE;
973+ STRCPY (pe -> pen_name , fname );
974+ pe -> pen_flags = 0 ;
975+ // run debuggy_find and capture return and forceit
976+ int f ;
977+ int lnum = debuggy_find (file , fname , (linenr_T )0 , & prof_ga , & f );
978+ if (lnum )
979+ {
980+ PEN_SET_PROFILING (pe );
981+ if (f )
982+ PEN_SET_FORCEIT (pe );
983+ }
984+ hash_add_item (ht , hi , pe -> pen_name , hash );
985+ #if defined(PROF_CACHE_LOG )
986+ ch_log (NULL , "has_profiling: %s %s forceit %s, profile %s" ,
987+ file ? "file" : "func" , fname ,
988+ PEN_IS_FORCEIT (pe ) ? "true" : "false" ,
989+ PEN_IS_PROFILING (pe ) ? "true" : "false" );
990+ #endif
991+ }
992+ else
993+ pe = HI2PE (hi );
994+ if (fp )
995+ * fp = PEN_IS_FORCEIT (pe );
996+ #if defined(PROF_CACHE_LOG )
997+ count_lookups [file ? PROF_HTAB_FILES : PROF_HTAB_FUNCS ]++ ;
998+ #endif
999+ return PEN_IS_PROFILING (pe );
1000+ }
1001+
1002+ static void
1003+ prof_clear_cache ()
1004+ {
1005+ if (!prof_cache_initialized )
1006+ {
1007+ hash_init (& prof_cache [PROF_HTAB_FUNCS ]);
1008+ hash_init (& prof_cache [PROF_HTAB_FILES ]);
1009+ prof_cache_initialized = TRUE;
1010+ }
1011+
1012+ hashtab_T * ht ;
1013+ for (ht = & prof_cache [0 ]; ht < & prof_cache [N_PROF_HTAB ]; ht ++ )
1014+ {
1015+ if (ht -> ht_used > 0 )
1016+ {
1017+ #if defined(PROF_CACHE_LOG )
1018+ int idx = ht == & prof_cache [PROF_HTAB_FUNCS ]
1019+ ? PROF_HTAB_FUNCS : PROF_HTAB_FILES ;
1020+ ch_log (NULL , "prof_clear_cache: %s, used: %ld, lookups: %d" ,
1021+ idx == PROF_HTAB_FUNCS ? "function" : "script" ,
1022+ ht -> ht_used , count_lookups [idx ]);
1023+ count_lookups [idx ] = 0 ;
1024+ #endif
1025+ hash_clear_all (ht , offsetof(profentry_T , pen_name ));
1026+ hash_init (ht );
1027+ }
1028+ }
9131029}
9141030#endif
9151031
@@ -979,6 +1095,10 @@ debuggy_find(
9791095 * fp = bp -> dbg_forceit ;
9801096 }
9811097 got_int |= prev_got_int ;
1098+ #ifdef FEAT_PROFILE
1099+ if (lnum && gap == & prof_ga )
1100+ break ;
1101+ #endif
9821102 }
9831103#ifdef FEAT_EVAL
9841104 else if (bp -> dbg_type == DBG_EXPR )
0 commit comments