示例#1
0
        /// <summary>
        /// Serializes pip graph fragment.
        /// </summary>
        public void Serialize(AbsolutePath filePath, IPipScheduleTraversal pipGraph, string fragmentDescription = null, bool useTopSortSerialization = false)
        {
            if (useTopSortSerialization)
            {
                var topSorter  = new PipGraphFragmentTopSort(pipGraph);
                var sortedPips = topSorter.Sort();

                SerializeTopSort(filePath, sortedPips, fragmentDescription);
            }
            else
            {
                SerializeSerially(filePath, pipGraph.RetrieveScheduledPips().ToList(), fragmentDescription);
            }
        }
示例#2
0
        private static bool SerializeFragmentIfRequested(
            PipGraphFragmentGeneratorConfiguration pipGraphFragmentGeneratorConfig,
            FrontEndContext context,
            IPipScheduleTraversal pipGraph)
        {
            Contract.Requires(context != null);
            Contract.Requires(pipGraph != null);

            if (!pipGraphFragmentGeneratorConfig.OutputFile.IsValid)
            {
                return(true);
            }

            try
            {
                var serializer = new PipGraphFragmentSerializer(context, new PipGraphFragmentContext())
                {
                    AlternateSymbolSeparator = pipGraphFragmentGeneratorConfig.AlternateSymbolSeparator
                };

                serializer.Serialize(
                    pipGraphFragmentGeneratorConfig.OutputFile,
                    pipGraph,
                    pipGraphFragmentGeneratorConfig.Description,
                    pipGraphFragmentGeneratorConfig.TopSort);

                Logger.Log.GraphFragmentSerializationStats(context.LoggingContext, serializer.FragmentDescription, serializer.Stats.ToString());

                return(true);
            }
            catch (Exception e)
            {
                Logger.Log.GraphFragmentExceptionOnSerializingFragment(
                    context.LoggingContext,
                    pipGraphFragmentGeneratorConfig.OutputFile.ToString(context.PathTable),
                    e.ToString());

                return(false);
            }
        }
示例#3
0
        private void WriteGraphNodeEntry(
            Pip pip,
            IPipScheduleTraversal graph,
            PipExecutionContext context,
            Dictionary <PipId, ProcessExecutionMonitoringReportedEventData> directoryInputContent,
            Dictionary <PipId, PipExecutionDirectoryOutputs> directoryOutputContent)
        {
            Contract.Requires(pip != null);

            m_writer.WriteWhitespace(Environment.NewLine);
            m_writer.WriteStartObject();
            {
                WriteStaticPipDetails(pip, context);
                {
                    // We get nice space savings by omitting dependsOn when it is empty (think HashSourceFile and WriteFile pips).
                    bool writtenHeader = false;

                    foreach (var dependency in graph.RetrievePipImmediateDependencies(pip))
                    {
                        if (!writtenHeader)
                        {
                            m_writer.WritePropertyName("dependsOn");
                            m_writer.WriteStartArray();

                            writtenHeader = true;
                        }

                        if (IncludePip(dependency.PipType))
                        {
                            m_writer.WriteValue(dependency.PipId.Value);
                        }
                    }

                    if (writtenHeader)
                    {
                        m_writer.WriteEndArray();
                    }

                    writtenHeader = false;

                    PipArtifacts.ForEachInput(pip, dependency =>
                    {
                        int dependencyId;
                        if (dependency.IsFile)
                        {
                            if (!m_fileIds.TryGetValue(dependency.FileArtifact.Path.ToString(context.PathTable), out dependencyId))
                            {
                                Contract.Assume(false, "Expected file artifact already written (dependency of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }

                            if (!writtenHeader)
                            {
                                m_writer.WritePropertyName("consumes");
                                m_writer.WriteStartArray();

                                writtenHeader = true;
                            }

                            m_writer.WriteValue(dependencyId);
                        }

                        return(true);
                    }, includeLazyInputs: true);

                    // Write reads from shared opaque directories
                    if (directoryInputContent.TryGetValue(pip.PipId, out var pipInput))
                    {
                        foreach (var input in pipInput.ReportedFileAccesses)
                        {
                            int dependencyId;
                            if (!m_fileIds.TryGetValue(input.ManifestPath.ToString(context.PathTable), out dependencyId))
                            {
                                // Ignore unrecognized reads (these are usually directories / files that aren't produced, so we don't have their ID)
                                continue;
                            }
                            m_writer.WriteValue(dependencyId);
                        }
                    }

                    if (writtenHeader)
                    {
                        m_writer.WriteEndArray();
                    }
                }

                {
                    m_writer.WritePropertyName("produces");
                    m_writer.WriteStartArray();

                    SortedSet <int> writes = new SortedSet <int>();

                    PipArtifacts.ForEachOutput(pip, dependency =>
                    {
                        int dependencyId;
                        if (dependency.IsFile)
                        {
                            if (!m_fileIds.TryGetValue(dependency.FileArtifact.Path.ToString(context.PathTable), out dependencyId))
                            {
                                Contract.Assume(false, "Expected file artifact already written (output of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }

                            writes.Add(dependencyId);
                        }

                        return(true);
                    }, includeUncacheable: true);

                    // Write outputs into shared opaque directories
                    if (directoryOutputContent.TryGetValue(pip.PipId, out var pipOutput))
                    {
                        foreach (var output in pipOutput.DirectoryOutputs)
                        {
                            DirectoryArtifact dir = output.directoryArtifact;
                            var content           = output.fileArtifactArray;

                            foreach (var file in content)
                            {
                                int dependencyId;
                                if (!m_fileIds.TryGetValue(file.Path.ToString(context.PathTable), out dependencyId))
                                {
                                    Contract.Assume(false, "Expected file artifact not found in fileId table " + file.Path.ToString(context.PathTable));
                                    throw new InvalidOperationException("Unreachable");
                                }
                                writes.Add(dependencyId);
                            }
                        }
                    }

                    foreach (int dependencyId in writes)
                    {
                        m_writer.WriteValue(dependencyId);
                    }

                    m_writer.WriteEndArray();
                }
            }

            m_writer.WriteEndObject();
        }
示例#4
0
        /// <summary>
        /// Save the graph. This method produces the whole file content. It must be called only once.
        /// </summary>
        public void SaveGraphData(
            IPipScheduleTraversal graph,
            PipExecutionContext context,
            IEnumerable <PipExecutionPerformanceEventData> executionData,
            IEnumerable <string> workers,
            IEnumerable <KeyValuePair <FileArtifact, FileContentInfo> > fileData,
            Dictionary <PipId, ProcessExecutionMonitoringReportedEventData> directoryInputContent,
            Dictionary <PipId, PipExecutionDirectoryOutputs> directoryOutputContent)
        {
            Contract.Requires(graph != null);
            Contract.Requires(context != null);
            Contract.Assume(m_fileIds.Count == 0);

            // We have transitioned to the Exporting state.
            // Observed execution info may get queued up for when we begin draining the queue (below).

            // Don't overlap pip ids with file/directory ids
            m_nextId = graph.PipCount + 1;

            m_writer.WriteStartObject(); // Outermost object

            WritePreamble();

            m_writer.WritePropertyName("artifacts");
            m_writer.WriteStartArray();

            // To save some work on pip deserialization (we are pathologically visiting every pip in the pip table),
            // we hold pips alive between the various passes.
            var pips = new List <Pip>();

            {
                var directoryPips = new List <SealDirectory>();

                // TODO: This is pretty expensive, as it loads all pips in memory
                foreach (Pip pip in graph.RetrieveScheduledPips())
                {
                    pips.Add(pip);

                    // Add all of the inputs to the file list
                    PipArtifacts.ForEachInput(pip, dependency =>
                    {
                        if (dependency.IsFile)
                        {
                            // Use producerId=0, if first referenced will indicate consumed source file
                            AssignFileIdAndWriteFileEntry(dependency.FileArtifact.Path.ToString(context.PathTable), null);
                        }
                        return(true);
                    }, includeLazyInputs: true);

                    // Now add all of the outputs
                    PipArtifacts.ForEachOutput(pip, output =>
                    {
                        if (output.IsFile)
                        {
                            AssignFileIdAndWriteFileEntry(output.FileArtifact.Path.ToString(context.PathTable), pip.PipId);
                        }

                        return(true);
                    },
                                               includeUncacheable: true);

                    // Record files that are written into SharedOpaqueDirectories
                    if (directoryOutputContent.TryGetValue(pip.PipId, out var pipOutput))
                    {
                        foreach (var output in pipOutput.DirectoryOutputs)
                        {
                            DirectoryArtifact dir = output.directoryArtifact;
                            var content           = output.fileArtifactArray;

                            foreach (var file in content)
                            {
                                AssignFileIdAndWriteFileEntry(file.Path.ToString(context.PathTable), pip.PipId);
                            }
                        }
                    }

                    // SealDirectory pips are the only ones with directory outputs. As part of the artifact entry for the directory output,
                    // we want to capture the membership of the directory. This means we want to have assigned IDs to all member files before
                    // writing out that list (for easier parsing). So, we defer writing out seal directory pips until all file artifacts have been visited.
                    if (pip.PipType == PipType.SealDirectory)
                    {
                        var directoryPip = (SealDirectory)pip;
                        Contract.Assume(directoryPip.IsInitialized);
                        directoryPips.Add(directoryPip);

                        // Add any file artifacts from the sealed directory, many will be
                        // outputs of process pips but some will be source files
                        foreach (FileArtifact directoryMember in directoryPip.Contents)
                        {
                            AssignFileIdAndWriteFileEntry(directoryMember.Path.ToString(context.PathTable), pip.PipId);
                        }
                    }
                }

                //// Now write out the directory entries
                //foreach (SealDirectory directoryPip in directoryPips)
                //{
                //    AssignDirectoryIdAndWriteDirectoryEntry(directoryPip.Directory, context, directoryPip.Contents);
                //}
            }

            m_writer.WriteEndArray();

            m_writer.WriteWhitespace(Environment.NewLine);
            m_writer.WritePropertyName("graph");
            m_writer.WriteStartArray();
            {
                // Note we are using the 'pips' list captured above rather than possibly deserializing pips again.
                foreach (Pip pip in pips)
                {
                    if (!IncludePip(pip.PipType))
                    {
                        continue;
                    }

                    WriteGraphNodeEntry(pip, graph, context, directoryInputContent, directoryOutputContent);
                }
            }

            m_writer.WriteEndArray();

            if (executionData != null)
            {
                m_writer.WriteWhitespace(Environment.NewLine);
                m_writer.WritePropertyName("execution");
                m_writer.WriteStartArray();

                // Begin draining the execution entry queue, now that we are in an allowed state for that.
                // Iteration will complete only after Finish() is called, which marks the queue as complete (no new items).
                foreach (var pipWithPerf in executionData)
                {
                    WriteExecutionEntry(pipWithPerf.PipId, pipWithPerf.ExecutionPerformance);
                }

                // End the execution: array
                m_writer.WriteEndArray();
            }

            if (workers != null)
            {
                m_writer.WriteWhitespace(Environment.NewLine);
                ExportWorkerDetails(workers);
            }

            if (fileData != null)
            {
                m_writer.WriteWhitespace(Environment.NewLine);
                ExportFileDetails(fileData, context);
            }

            // End the outermost object
            m_writer.WriteWhitespace(Environment.NewLine);
            m_writer.WriteEndObject();
            m_writer.Flush();
        }
示例#5
0
        private void WriteGraphNodeEntry(
            Pip pip,
            IPipScheduleTraversal graph,
            PipExecutionContext context,
            Dictionary <DirectoryArtifact, int> directoryIds)
        {
            Contract.Requires(pip != null);

            m_writer.WriteWhitespace(Environment.NewLine);
            m_writer.WriteStartObject();
            {
                WriteStaticPipDetails(pip, context);
                {
                    // We get nice space savings by omitting dependsOn when it is empty (think HashSourceFile and WriteFile pips).
                    bool writtenHeader = false;

                    foreach (var dependency in graph.RetrievePipImmediateDependencies(pip))
                    {
                        if (!writtenHeader)
                        {
                            m_writer.WritePropertyName("dependsOn");
                            m_writer.WriteStartArray();

                            writtenHeader = true;
                        }

                        if (IncludePip(dependency.PipType))
                        {
                            m_writer.WriteValue(dependency.PipId.Value);
                        }
                    }

                    if (writtenHeader)
                    {
                        m_writer.WriteEndArray();
                    }

                    writtenHeader = false;

                    PipArtifacts.ForEachInput(pip, dependency =>
                    {
                        int dependencyId;
                        if (dependency.IsFile)
                        {
                            if (!m_fileIds.TryGetValue(dependency.FileArtifact, out dependencyId))
                            {
                                Contract.Assume(false, "Expected file artifact already written (dependency of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }
                        }
                        else
                        {
                            if (!directoryIds.TryGetValue(dependency.DirectoryArtifact, out dependencyId))
                            {
                                Contract.Assume(false, "Expected directory artifact already written (input of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }
                        }

                        if (!writtenHeader)
                        {
                            m_writer.WritePropertyName("consumes");
                            m_writer.WriteStartArray();

                            writtenHeader = true;
                        }

                        m_writer.WriteValue(dependencyId);
                        return(true);
                    }, includeLazyInputs: true);

                    if (writtenHeader)
                    {
                        m_writer.WriteEndArray();
                    }
                }

                {
                    m_writer.WritePropertyName("produces");
                    m_writer.WriteStartArray();

                    PipArtifacts.ForEachOutput(pip, dependency =>
                    {
                        int dependencyId;
                        if (dependency.IsFile)
                        {
                            if (!m_fileIds.TryGetValue(dependency.FileArtifact, out dependencyId))
                            {
                                Contract.Assume(false, "Expected file artifact already written (output of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }
                        }
                        else
                        {
                            if (!directoryIds.TryGetValue(dependency.DirectoryArtifact, out dependencyId))
                            {
                                Contract.Assume(false, "Expected directory artifact already written (output of pip) " + dependency.Path.ToString(context.PathTable));
                                throw new InvalidOperationException("Unreachable");
                            }
                        }

                        m_writer.WriteValue(dependencyId);
                        return(true);
                    }, includeUncacheable: true);

                    m_writer.WriteEndArray();
                }
            }

            m_writer.WriteEndObject();
        }
示例#6
0
        /// <summary>
        /// Save the graph. This method produces the whole file content. It must be called only once.
        /// </summary>
        public void SaveGraphData(
            IPipScheduleTraversal graph,
            PipExecutionContext context,
            IEnumerable <PipExecutionPerformanceEventData> executionData,
            IEnumerable <string> workers,
            IEnumerable <KeyValuePair <FileArtifact, FileContentInfo> > fileData)
        {
            Contract.Requires(graph != null);
            Contract.Requires(context != null);
            Contract.Assume(m_fileIds.Count == 0);

            // We have transitioned to the Exporting state.
            // Observed execution info may get queued up for when we begin draining the queue (below).

            // Don't overlap pip ids with file/directory ids
            int nextId       = graph.PipCount + 1;
            var directoryIds = new Dictionary <DirectoryArtifact, int>(capacity: 1000);

            m_writer.WriteStartObject(); // Outermost object

            WritePreamble();

            m_writer.WritePropertyName("artifacts");
            m_writer.WriteStartArray();

            // To save some work on pip deserialization (we are pathologically visiting every pip in the pip table),
            // we hold pips alive between the various passes.
            var pips = new List <Pip>();

            {
                var directoryPips = new List <SealDirectory>();

                // TODO: This is pretty expensive, as it loads all pips in memory
                foreach (Pip pip in graph.RetrieveScheduledPips())
                {
                    pips.Add(pip);

                    PipArtifacts.ForEachInput(pip, input =>
                    {
                        if (input.IsFile && input.FileArtifact.IsSourceFile)
                        {
                            AssignFileIdAndWriteFileEntry(input.FileArtifact, context, ref nextId);
                        }

                        return(true);
                    },
                                              includeLazyInputs: true
                                              );

                    PipArtifacts.ForEachOutput(pip, output =>
                    {
                        if (output.IsFile)
                        {
                            AssignFileIdAndWriteFileEntry(output.FileArtifact, context, ref nextId);
                        }

                        return(true);
                    },
                                               includeUncacheable: true);

                    // SealDirectory pips are the only ones with directory outputs. As part of the artifact entry for the directory output,
                    // we want to capture the membership of the directory. This means we want to have assigned IDs to all member files before
                    // writing out that list (for easier parsing). So, we defer writing out seal directory pips until all file artifacts have been visited.
                    if (pip.PipType == PipType.SealDirectory)
                    {
                        var directoryPip = (SealDirectory)pip;
                        Contract.Assume(directoryPip.IsInitialized);

                        foreach (FileArtifact f in directoryPip.Contents)
                        {
                            AssignFileIdAndWriteFileEntry(f, context, ref nextId);
                        }

                        directoryPips.Add(directoryPip);
                    }
                }

                foreach (SealDirectory directoryPip in directoryPips)
                {
                    AssignDirectoryIdAndWriteDirectoryEntry(directoryPip.Directory, context, directoryPip.Contents, directoryIds, ref nextId);
                }
            }

            m_writer.WriteEndArray();

            m_writer.WritePropertyName("graph");
            m_writer.WriteStartArray();
            {
                // Note we are using the 'pips' list captured above rather than possibly deserializing pips again.
                foreach (Pip pip in pips)
                {
                    if (!IncludePip(pip.PipType))
                    {
                        continue;
                    }

                    WriteGraphNodeEntry(pip, graph, context, directoryIds);
                }
            }

            m_writer.WriteEndArray();

            // Avoid holding a reference to this map if it isn't later needed by the special file details exporter
            if (fileData == null)
            {
                m_fileIds = null;
            }

            if (executionData != null)
            {
                m_writer.WritePropertyName("execution");
                m_writer.WriteStartArray();

                // Begin draining the execution entry queue, now that we are in an allowed state for that.
                // Iteration will complete only after Finish() is called, which marks the queue as complete (no new items).
                foreach (var pipWithPerf in executionData)
                {
                    WriteExecutionEntry(pipWithPerf.PipId, pipWithPerf.ExecutionPerformance);
                }

                // End the execution: array
                m_writer.WriteEndArray();
            }

            if (workers != null)
            {
                ExportWorkerDetails(workers);
            }

            if (fileData != null)
            {
                ExportFileDetails(fileData);
            }

            // End the outermost object
            m_writer.WriteEndObject();
            m_writer.Flush();
        }
示例#7
0
 /// <summary>
 /// Create an instance of <see cref="PipGraphFragmentTopSort"/>/
 /// </summary>
 /// <param name="pipGraph">Pip graph.</param>
 public PipGraphFragmentTopSort(IPipScheduleTraversal pipGraph)
 {
     Contract.Requires(pipGraph != null);
     m_pipGraph = pipGraph;
 }
示例#8
0
 /// <nodoc />
 public ScopePipGraph(IPipScheduleTraversal graph)
 {
     Graph = graph;
 }
示例#9
0
        public ObjectInfo GetObjectInfo(object context, object obj)
        {
            obj = obj is EvaluationResult evalResult
                ? evalResult.Value
                : obj;

            if (obj == null || IsInvalid(obj))
            {
                return(s_nullObj);
            }

            if (obj.GetType().IsArray)
            {
                return(ArrayObjInfo(((IEnumerable)obj).Cast <object>().ToArray()));
            }

            var customResult = m_customRenderer?.Invoke(this, context, obj);

            if (customResult != null)
            {
                return(customResult);
            }

            return(obj switch
            {
                ScopeLocals scope => new ObjectInfo(LocalsScopeName, null, Lazy.Create(() => GetLocalsForStackEntry(scope.EvalState, scope.FrameIndex))),
                ScopePipGraph scope => PipGraphInfo(scope.Graph).WithPreview(PipGraphScopeName),
                ScopeAllModules scope => ArrayObjInfo(scope.EvaluatedModules.ToArray()).WithPreview(EvaluatedModulesScopeName),
                IModuleAndContext mc => GetObjectInfo(mc.Tree.RootContext, mc.Module),
                ObjectInfo objInf => objInf,
                IPipScheduleTraversal graph => PipGraphInfo(graph),
                Pip pip => GenericObjectInfo(pip, $"<{pip.PipType}>").Build(),
                PipProvenance prov => ProvenanceInfo(prov),
                EnvironmentVariable envVar => EnvironmentVariableInfo(envVar),
                PipFragment pipFrag => PipFragmentInfo(context, pipFrag),
                Thunk thunk => thunk.Value != null?GetObjectInfo(context, thunk.Value) : new ObjectInfo("<not evaluated>"),
                    FunctionLikeExpression lambda => LambdaInfo(lambda),
                    Closure cls => LambdaInfo(cls.Function),
                    SymbolAtom sym => new ObjectInfo(sym.ToString(StringTable)),
                    StringId id => new ObjectInfo(id.ToString(StringTable)),
                    PipId id => new ObjectInfo($"{id.Value}"),
                    UndefinedLiteral _ => new ObjectInfo("undefined", UndefinedLiteral.Instance),
                    UndefinedValue _ => new ObjectInfo("undefined", UndefinedValue.Instance),
                    AbsolutePath path => new ObjectInfo($"p`{path.ToString(PathTable)}`", path),
                    RelativePath path => new ObjectInfo($"r`{path.ToString(StringTable)}`", path),
                    PathAtom atom => new ObjectInfo($"a`{atom.ToString(StringTable)}`", atom),
                    FileArtifact file => new ObjectInfo($"f`{file.Path.ToString(PathTable)}`", file),
                    DirectoryArtifact dir => new ObjectInfo($"d`{dir.Path.ToString(PathTable)}`", dir),
                    int num => new ObjectInfo($"{num}"),
                    uint num => new ObjectInfo($"{num}"),
                    short num => new ObjectInfo($"{num}", (int)num),
                    long num => new ObjectInfo($"{num}"),
                    char ch => new ObjectInfo($"'{ch}'", ch.ToString()),
                    string str => new ObjectInfo($"\"{str}\"", str),
                    Enum e => new ObjectInfo($"{e.GetType().Name}.{e}", e),
                    NumberLiteral numLit => new ObjectInfo(numLit.UnboxedValue.ToString(), numLit),
                    Func <object> func => FuncObjInfo(func),
                    ArraySegment <object> arrSeg => ArrayObjInfo(arrSeg),
                    IEnumerable enu => new ObjectInfoBuilder().Preview("IEnumerable").Prop("Result", Lazy.Create <object>(() => enu.Cast <object>().ToArray())).Build(),
                    ArrayLiteral arrLit => ArrayObjInfo(arrLit.Values.Select(v => v.Value).ToArray()).WithOriginal(arrLit),
                    ModuleBinding binding => GetObjectInfo(context, binding.Body),
                    ErrorValue error => ErrorValueInfo(),
                    object o => GenericObjectInfo(o).Build(),
                    _ => s_nullObj
            });