public virtual void ExtractContextAndRedisCommand(ClrThread thread, ThreadStackEnumerator threadStackObjectEnumerator, IModelMapperFactory factory, IDictionary <string, List <HttpContextMappingModel> > contextsByRedisCommands)
        {
            string command = null;
            HttpContextMappingModel httpContext = null;

            foreach (var clrObj in threadStackObjectEnumerator.Enumerate(thread))
            {
                ExtractCommandOrContext(clrObj, factory, ref command, ref httpContext);

                // already extracted both from the thread stack.
                if (!string.IsNullOrEmpty(command) && (httpContext != null))
                {
                    break;
                }
            }

            List <HttpContextMappingModel> httpContextsForGivenRedisCommand = null;

            if (!string.IsNullOrEmpty(command))
            {
                if (!contextsByRedisCommands.ContainsKey(command))
                {
                    httpContextsForGivenRedisCommand = new List <HttpContextMappingModel>();
                    contextsByRedisCommands.Add(command, httpContextsForGivenRedisCommand);
                }
                else
                {
                    httpContextsForGivenRedisCommand = contextsByRedisCommands[command];
                }
            }

            if (httpContext?.HasURL == true && httpContext?.Request != null)
            {
                if (httpContextsForGivenRedisCommand != null)
                {
                    httpContextsForGivenRedisCommand.Add(httpContext);
                }
                else
                {
                    if (!contextsByRedisCommands.ContainsKey("NoCommand"))
                    {
                        contextsByRedisCommands.Add("NoCommand", new List <HttpContextMappingModel>());
                    }

                    var noCommand = contextsByRedisCommands["NoCommand"];
                    noCommand.Add(httpContext);
                }
            }
        }
        public virtual StringBuilder ExtractRedisPendingCommandsForRequests(ClrRuntime runtime, IModelMapperFactory clrObjectToModelFactory)
        {
            var knownObjects = new HashSet <ulong>();

            var threadStackObjectEnumerator = new ThreadStackEnumerator(
                includePossiblyDead: true,
                filter: ThreadStackObjectFilter);

            List <string> commands = new List <string>();

            int totalContexts = 0;
            IDictionary <string, List <HttpContextMappingModel> > httpContextsMappedByRedisCommand = new Dictionary <string, List <HttpContextMappingModel> >();

            foreach (var contextThread in ThreadFilter.ExtactAliveUserThreads(runtime))
            {
                ExtractContextAndRedisCommand(contextThread, threadStackObjectEnumerator, clrObjectToModelFactory, httpContextsMappedByRedisCommand);
            }

            var differentRedisCommands = httpContextsMappedByRedisCommand.OrderByDescending(grouping => grouping.Value.Count);

            var aspNetIDs = (from requestGroup in httpContextsMappedByRedisCommand
                             let httpContexts = requestGroup.Value
                                                from httpContext in httpContexts
                                                let requestInfo = new { httpContext, httpContext?.Request?.AspNetSessionId }
                             group requestInfo by requestInfo.AspNetSessionId into sameSessions
                             select sameSessions).OrderByDescending(t => t.Count()).ToArray();

            var sb = new StringBuilder();

            sb.AppendLine($"Different Commands found: {differentRedisCommands.Count()}");

            totalContexts = httpContextsMappedByRedisCommand.Sum(elem => elem.Value.Count);

            sb.AppendLine($"Total requests {totalContexts} VS unique asp.net sessions {aspNetIDs.Length}");

            foreach (var cmd in differentRedisCommands)
            {
                sb.AppendLine($"Hits: {cmd.Value.Count}");

                sb.AppendLine();
                sb.AppendLine(cmd.Key);

                foreach (var context in cmd.Value.OrderByDescending(request => request.ExecutionDuration))
                {
                    sb.AppendLine(FormatLine(context));
                }
                sb.AppendLine("**************");
            }

            foreach (var session in aspNetIDs)
            {
                sb.AppendLine($"Session: {session.Key}:");
                foreach (var request in session)
                {
                    sb.AppendLine($"{request.httpContext.URL} executed for {request.httpContext.ExecutionDuration.TotalSeconds:F2} sec.");
                }
                sb.AppendLine("**************");
                sb.AppendLine();
            }
            return(sb);
        }