Merge remote-tracking branch 'origin/master' into sdl_platform

This commit is contained in:
2025-06-16 14:09:29 +03:00
65 changed files with 3633 additions and 880 deletions

View File

@@ -10,11 +10,9 @@ using FlaxEngine;
namespace FlaxEditor.Content
{
/// <summary>
/// Context proxy object for shader source files (represented by <see cref="ShaderSourceItem"/>).
/// Base class for shader source files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Source")]
public class ShaderSourceProxy : ContentProxy
public abstract class ShaderBaseProxy : ContentProxy
{
/// <inheritdoc />
public override bool CanCreate(ContentFolder targetLocation)
@@ -29,6 +27,21 @@ namespace FlaxEditor.Content
return targetLocation.ShortName == "Source" && prevTargetLocation.ShortName == "Shaders";
}
/// <inheritdoc />
public override EditorWindow Open(Editor editor, ContentItem item)
{
Editor.Instance.CodeEditing.OpenFile(item.Path);
return null;
}
}
/// <summary>
/// Context proxy object for shader source files (represented by <see cref="ShaderSourceItem"/>).
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Source (.shader)")]
public class ShaderSourceProxy : ShaderBaseProxy
{
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
@@ -44,13 +57,6 @@ namespace FlaxEditor.Content
File.WriteAllText(outputPath, shaderTemplate, Encoding.UTF8);
}
/// <inheritdoc />
public override EditorWindow Open(Editor editor, ContentItem item)
{
Editor.Instance.CodeEditing.OpenFile(item.Path);
return null;
}
/// <inheritdoc />
public override Color AccentColor => Color.FromRGB(0x7542f5);
@@ -66,4 +72,33 @@ namespace FlaxEditor.Content
return item is ShaderSourceItem;
}
}
/// <summary>
/// Context proxy object for shader header files.
/// </summary>
/// <seealso cref="FlaxEditor.Content.ContentProxy" />
[ContentContextMenu("New/Shader Header (.hlsl)")]
public class ShaderHeaderProxy : ShaderBaseProxy
{
/// <inheritdoc />
public override void Create(string outputPath, object arg)
{
File.WriteAllText(outputPath, "\n", Encoding.UTF8);
}
/// <inheritdoc />
public override Color AccentColor => Color.FromRGB(0x2545a5);
/// <inheritdoc />
public override string FileExtension => "hlsl";
/// <inheritdoc />
public override string Name => "Shader Header";
/// <inheritdoc />
public override bool IsProxyFor(ContentItem item)
{
return false;
}
}
}

View File

@@ -1,6 +1,5 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.Content.Settings;
using FlaxEngine;
@@ -16,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
{
private int _layersCount;
private List<CheckBox> _checkBoxes;
private VerticalPanel _upperRightCell;
private VerticalPanel _bottomLeftCell;
private UniformGridPanel _grid;
private Border _horizontalHighlight;
private Border _verticalHighlight;
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
@@ -37,12 +41,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = panel,
};
var style = FlaxEngine.GUI.Style.Current;
_horizontalHighlight = new Border()
{
Parent = panel,
BorderColor = style.Foreground,
BorderWidth = 1.0f,
Visible = false,
};
_verticalHighlight = new Border()
{
Parent = panel,
BorderColor = style.Foreground,
BorderWidth = 1.0f,
Visible = false,
};
var upperLeftCell = new Label
{
Parent = gridPanel,
};
var upperRightCell = new VerticalPanel
_upperRightCell = new VerticalPanel
{
ClipChildren = false,
Pivot = new Float2(0.00001f, 0.0f),
@@ -54,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = gridPanel,
};
var bottomLeftCell = new VerticalPanel
_bottomLeftCell = new VerticalPanel
{
Pivot = Float2.Zero,
Spacing = 0,
@@ -63,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
Parent = gridPanel,
};
var grid = new UniformGridPanel(0)
_grid = new UniformGridPanel(0)
{
SlotsHorizontally = layersCount,
SlotsVertically = layersCount,
@@ -74,13 +95,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
int layerIndex = 0;
for (; layerIndex < layerNames.Length; layerIndex++)
{
upperRightCell.AddChild(new Label
_upperRightCell.AddChild(new Label
{
Height = labelsHeight,
Text = layerNames[layerNames.Length - layerIndex - 1],
HorizontalAlignment = TextAlignment.Near,
});
bottomLeftCell.AddChild(new Label
_bottomLeftCell.AddChild(new Label
{
Height = labelsHeight,
Text = layerNames[layerIndex],
@@ -90,13 +111,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
for (; layerIndex < layersCount; layerIndex++)
{
string name = "Layer " + layerIndex;
upperRightCell.AddChild(new Label
_upperRightCell.AddChild(new Label
{
Height = labelsHeight,
Text = name,
HorizontalAlignment = TextAlignment.Near,
});
bottomLeftCell.AddChild(new Label
_bottomLeftCell.AddChild(new Label
{
Height = labelsHeight,
Text = name,
@@ -118,7 +139,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
var box = new CheckBox(0, 0, true)
{
Tag = new Float2(_layersCount - column - 1, row),
Parent = grid,
Parent = _grid,
Checked = GetBit(column, row),
};
box.StateChanged += OnCheckBoxChanged;
@@ -126,7 +147,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
}
for (; column < layersCount; column++)
{
grid.AddChild(new Label());
_grid.AddChild(new Label());
}
}
}
@@ -141,6 +162,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
/// <inheritdoc />
public override void Refresh()
{
int selectedColumn = -1;
int selectedRow = -1;
var style = FlaxEngine.GUI.Style.Current;
bool mouseOverGrid = _grid.IsMouseOver;
// Only hide highlights if mouse is not over the grid to reduce flickering
if (!mouseOverGrid)
{
_horizontalHighlight.Visible = false;
_verticalHighlight.Visible = false;
}
// Sync check boxes
for (int i = 0; i < _checkBoxes.Count; i++)
{
@@ -148,6 +181,39 @@ namespace FlaxEditor.CustomEditors.Dedicated
int column = (int)((Float2)box.Tag).X;
int row = (int)((Float2)box.Tag).Y;
box.Checked = GetBit(column, row);
if (box.IsMouseOver)
{
selectedColumn = column;
selectedRow = row;
_horizontalHighlight.X = _grid.X - _bottomLeftCell.Width;
_horizontalHighlight.Y = _grid.Y + box.Y;
_horizontalHighlight.Width = _bottomLeftCell.Width + box.Width + box.X;
_horizontalHighlight.Height = box.Height;
_horizontalHighlight.Visible = true;
_verticalHighlight.X = _grid.X + box.X;
_verticalHighlight.Y = _grid.Y - _upperRightCell.Height;
_verticalHighlight.Width = box.Width;
_verticalHighlight.Height = _upperRightCell.Height + box.Height + box.Y;
_verticalHighlight.Visible = true;
}
}
for (int i = 0; i < _checkBoxes.Count; i++)
{
var box = _checkBoxes[i];
int column = (int)((Float2)box.Tag).X;
int row = (int)((Float2)box.Tag).Y;
if (!mouseOverGrid)
box.ImageColor = style.BorderSelected;
else if (selectedColumn > -1 && selectedRow > -1)
{
bool isRowOrColumn = column == selectedColumn || row == selectedRow;
box.ImageColor = style.BorderSelected * (isRowOrColumn ? 1.2f : 0.75f);
}
}
}

View File

@@ -563,8 +563,17 @@ namespace FlaxEditor.GUI
case KeyboardKeys.Escape:
Hide();
return true;
case KeyboardKeys.Backspace:
// Alow the user to quickly focus the searchbar
if (_searchBox != null && !_searchBox.IsFocused)
{
_searchBox.Focus();
_searchBox.SelectAll();
return true;
}
break;
case KeyboardKeys.ArrowDown:
{
case KeyboardKeys.ArrowUp:
if (RootWindow.FocusedControl == null)
{
// Focus search box if nothing is focused
@@ -572,39 +581,17 @@ namespace FlaxEditor.GUI
return true;
}
// Focus the first visible item or then next one
// Get the next item
var items = GetVisibleItems();
var focusedIndex = items.IndexOf(focusedItem);
if (focusedIndex == -1)
focusedIndex = -1;
if (focusedIndex + 1 < items.Count)
{
var item = items[focusedIndex + 1];
item.Focus();
_scrollPanel.ScrollViewTo(item);
return true;
}
break;
}
case KeyboardKeys.ArrowUp:
if (focusedItem != null)
{
// Focus the previous visible item or the search box
var items = GetVisibleItems();
var focusedIndex = items.IndexOf(focusedItem);
if (focusedIndex == 0)
{
_searchBox?.Focus();
}
else if (focusedIndex > 0)
{
var item = items[focusedIndex - 1];
item.Focus();
_scrollPanel.ScrollViewTo(item);
return true;
}
}
break;
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
var nextItem = items[nextIndex];
// Focus the next item
nextItem.Focus();
_scrollPanel.ScrollViewTo(nextItem);
return true;
case KeyboardKeys.Return:
if (focusedItem != null)
{

View File

@@ -268,6 +268,19 @@ namespace FlaxEditor.GUI
return AddChild(new MainMenuButton(text));
}
/// <summary>
/// Gets or adds a button.
/// </summary>
/// <param name="text">The button text</param>
/// <returns>The existing or created button control.</returns>
public MainMenuButton GetOrAddButton(string text)
{
MainMenuButton result = GetButton(text);
if (result == null)
result = AddButton(text);
return result;
}
/// <summary>
/// Gets the button.
/// </summary>

View File

@@ -122,6 +122,14 @@ namespace FlaxEditor.GUI
return this;
}
private void OnClicked()
{
if (AutoCheck)
Checked = !Checked;
Clicked?.Invoke();
(Parent as ToolStrip)?.OnButtonClicked(this);
}
/// <inheritdoc />
public override void Draw()
{
@@ -196,11 +204,7 @@ namespace FlaxEditor.GUI
if (button == MouseButton.Left && _primaryMouseDown)
{
_primaryMouseDown = false;
if (AutoCheck)
Checked = !Checked;
Clicked?.Invoke();
(Parent as ToolStrip)?.OnButtonClicked(this);
OnClicked();
return true;
}
if (button == MouseButton.Right && _secondaryMouseDown)
@@ -215,6 +219,18 @@ namespace FlaxEditor.GUI
return base.OnMouseUp(location, button);
}
/// <inheritdoc />
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
{
if (button == MouseButton.Left)
{
OnClicked();
return true;
}
return false;
}
/// <inheritdoc />
public override void OnMouseLeave()
{

View File

@@ -1135,6 +1135,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new FontProxy());
Proxy.Add(new ShaderProxy());
Proxy.Add(new ShaderSourceProxy());
Proxy.Add(new ShaderHeaderProxy());
Proxy.Add(new ParticleEmitterProxy());
Proxy.Add(new ParticleEmitterFunctionProxy());
Proxy.Add(new ParticleSystemProxy());

View File

@@ -139,6 +139,10 @@ namespace FlaxEditor.Options
[EditorDisplay("Common"), EditorOrder(240)]
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
[DefaultValue(typeof(InputBinding), "Ctrl+BackQuote")]
[EditorDisplay("Common"), EditorOrder(250)]
public InputBinding FocusConsoleCommand = new InputBinding(KeyboardKeys.BackQuote, KeyboardKeys.Control);
#endregion
#region File
@@ -647,5 +651,45 @@ namespace FlaxEditor.Options
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
#endregion
#region Node editors
[DefaultValue(typeof(InputBinding), "Shift+W")]
[EditorDisplay("Node editors"), EditorOrder(4500)]
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+A")]
[EditorDisplay("Node editors"), EditorOrder(4510)]
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+S")]
[EditorDisplay("Node editors"), EditorOrder(4520)]
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Shift+D")]
[EditorDisplay("Node editors"), EditorOrder(4530)]
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
[EditorDisplay("Node editors"), EditorOrder(4540)]
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
[EditorDisplay("Node editors"), EditorOrder(4550)]
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
[DefaultValue(typeof(InputBinding), "Q")]
[EditorDisplay("Node editors"), EditorOrder(4560)]
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
[DefaultValue(typeof(InputBinding), "None")]
[EditorDisplay("Node editors"), EditorOrder(4570)]
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
[DefaultValue(typeof(InputBinding), "None")]
[EditorDisplay("Node editors"), EditorOrder(4580)]
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
#endregion
}
}

View File

@@ -0,0 +1,43 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Surface
{
/// <summary>
/// Node Alignment type
/// </summary>
[HideInEditor]
public enum NodeAlignmentType
{
/// <summary>
/// Align nodes vertically to top, matching top-most node
/// </summary>
Top,
/// <summary>
/// Align nodes vertically to middle, using average of all nodes
/// </summary>
Middle,
/// <summary>
/// Align nodes vertically to bottom, matching bottom-most node
/// </summary>
Bottom,
/// <summary>
/// Align nodes horizontally to left, matching left-most node
/// </summary>
Left,
/// <summary>
/// Align nodes horizontally to center, using average of all nodes
/// </summary>
Center,
/// <summary>
/// Align nodes horizontally to right, matching right-most node
/// </summary>
Right,
}
}

View File

@@ -191,7 +191,16 @@ namespace FlaxEditor.Surface
private ContextMenuButton _cmCopyButton;
private ContextMenuButton _cmDuplicateButton;
private ContextMenuChildMenu _cmFormatNodesMenu;
private ContextMenuButton _cmFormatNodesConnectionButton;
private ContextMenuButton _cmAlignNodesTopButton;
private ContextMenuButton _cmAlignNodesMiddleButton;
private ContextMenuButton _cmAlignNodesBottomButton;
private ContextMenuButton _cmAlignNodesLeftButton;
private ContextMenuButton _cmAlignNodesCenterButton;
private ContextMenuButton _cmAlignNodesRightButton;
private ContextMenuButton _cmDistributeNodesHorizontallyButton;
private ContextMenuButton _cmDistributeNodesVerticallyButton;
private ContextMenuButton _cmRemoveNodeConnectionsButton;
private ContextMenuButton _cmRemoveBoxConnectionsButton;
private readonly Float2 ContextMenuOffset = new Float2(5);
@@ -399,8 +408,24 @@ namespace FlaxEditor.Surface
}
menu.AddSeparator();
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); });
_cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection;
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
_cmAlignNodesMiddleButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align middle", Editor.Instance.Options.Options.Input.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); });
_cmAlignNodesBottomButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align bottom", Editor.Instance.Options.Options.Input.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmAlignNodesLeftButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align left", Editor.Instance.Options.Options.Input.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); });
_cmAlignNodesCenterButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align center", Editor.Instance.Options.Options.Input.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); });
_cmAlignNodesRightButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align right", Editor.Instance.Options.Options.Input.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); });
_cmFormatNodesMenu.ContextMenu.AddSeparator();
_cmDistributeNodesHorizontallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute horizontally", Editor.Instance.Options.Options.Input.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); });
_cmDistributeNodesVerticallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute vertically", Editor.Instance.Options.Options.Input.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); });
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>
{

View File

@@ -282,5 +282,122 @@ namespace FlaxEditor.Surface
return maxOffset;
}
/// <summary>
/// Align given nodes on a graph using the given alignment type.
/// Ignores any potential overlap.
/// </summary>
/// <param name="nodes">List of nodes</param>
/// <param name="alignmentType">Alignemnt type</param>
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
{
if(nodes.Count <= 1)
return;
var undoActions = new List<MoveNodesAction>();
var boundingBox = GetNodesBounds(nodes);
for(int i = 0; i < nodes.Count; i++)
{
var centerY = boundingBox.Center.Y - (nodes[i].Height / 2);
var centerX = boundingBox.Center.X - (nodes[i].Width / 2);
var newLocation = alignmentType switch
{
NodeAlignmentType.Top => new Float2(nodes[i].Location.X, boundingBox.Top),
NodeAlignmentType.Middle => new Float2(nodes[i].Location.X, centerY),
NodeAlignmentType.Bottom => new Float2(nodes[i].Location.X, boundingBox.Bottom - nodes[i].Height),
NodeAlignmentType.Left => new Float2(boundingBox.Left, nodes[i].Location.Y),
NodeAlignmentType.Center => new Float2(centerX, nodes[i].Location.Y),
NodeAlignmentType.Right => new Float2(boundingBox.Right - nodes[i].Width, nodes[i].Location.Y),
_ => throw new NotImplementedException($"Unsupported node alignment type: {alignmentType}"),
};
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
if(Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, $"Align nodes ({alignmentType})"));
}
/// <summary>
/// Distribute the given nodes as equally as possible inside the bounding box, if no fit can be done it will use a default pad of 10 pixels between nodes.
/// </summary>
/// <param name="nodes">List of nodes</param>
/// <param name="vertically">If false will be done horizontally, if true will be done vertically</param>
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
{
if(nodes.Count <= 1)
return;
var undoActions = new List<MoveNodesAction>();
var boundingBox = GetNodesBounds(nodes);
float padding = 10;
float totalSize = 0;
for (int i = 0; i < nodes.Count; i++)
{
if (vertically)
{
totalSize += nodes[i].Height;
}
else
{
totalSize += nodes[i].Width;
}
}
if(vertically)
{
nodes.Sort((leftValue, rightValue) => { return leftValue.Y.CompareTo(rightValue.Y); });
float position = boundingBox.Top;
if(totalSize < boundingBox.Height)
{
padding = (boundingBox.Height - totalSize) / nodes.Count;
}
for(int i = 0; i < nodes.Count; i++)
{
var newLocation = new Float2(nodes[i].X, position);
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
position += nodes[i].Height + padding;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
else
{
nodes.Sort((leftValue, rightValue) => { return leftValue.X.CompareTo(rightValue.X); });
float position = boundingBox.Left;
if(totalSize < boundingBox.Width)
{
padding = (boundingBox.Width - totalSize) / nodes.Count;
}
for(int i = 0; i < nodes.Count; i++)
{
var newLocation = new Float2(position, nodes[i].Y);
var locationDelta = newLocation - nodes[i].Location;
nodes[i].Location = newLocation;
position += nodes[i].Width + padding;
if (Undo != null)
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
}
}
MarkAsEdited(false);
Undo?.AddAction(new MultiUndoAction(undoActions, vertically ? "Distribute nodes vertically" : "Distribute nodes horizontally"));
}
}
}

View File

@@ -27,6 +27,7 @@ namespace FlaxEditor.Surface
private Float2 _movingNodesDelta;
private Float2 _gridRoundingDelta;
private HashSet<SurfaceNode> _movingNodes;
private HashSet<SurfaceNode> _temporarySelectedNodes;
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
private class InputBracket
@@ -130,13 +131,34 @@ namespace FlaxEditor.Surface
if (_rootControl.Children[i] is SurfaceControl control)
{
var select = control.IsSelectionIntersecting(ref selectionRect);
if (select != control.IsSelected)
if (Root.GetKey(KeyboardKeys.Shift))
{
control.IsSelected = select;
selectionChanged = true;
if (select == control.IsSelected && _temporarySelectedNodes.Contains(control))
{
control.IsSelected = !select;
selectionChanged = true;
}
}
else if (Root.GetKey(KeyboardKeys.Control))
{
if (select != control.IsSelected && !_temporarySelectedNodes.Contains(control))
{
control.IsSelected = select;
selectionChanged = true;
}
}
else
{
if (select != control.IsSelected)
{
control.IsSelected = select;
selectionChanged = true;
}
}
}
}
if (selectionChanged)
SelectionChanged?.Invoke();
}
@@ -461,6 +483,19 @@ namespace FlaxEditor.Surface
// Cache data
_isMovingSelection = false;
_mousePos = location;
if(_temporarySelectedNodes == null)
_temporarySelectedNodes = new HashSet<SurfaceNode>();
else
_temporarySelectedNodes.Clear();
for (int i = 0; i < _rootControl.Children.Count; i++)
{
if (_rootControl.Children[i] is SurfaceNode node && node.IsSelected)
{
_temporarySelectedNodes.Add(node);
}
}
if (button == MouseButton.Left)
{
_leftMouseDown = true;
@@ -488,9 +523,11 @@ namespace FlaxEditor.Surface
// Check if user is pressing control
if (Root.GetKey(KeyboardKeys.Control))
{
// Add/remove from selection
controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected;
SelectionChanged?.Invoke();
AddToSelection(controlUnderMouse);
}
else if (Root.GetKey(KeyboardKeys.Shift))
{
RemoveFromSelection(controlUnderMouse);
}
// Check if node isn't selected
else if (!controlUnderMouse.IsSelected)
@@ -500,10 +537,14 @@ namespace FlaxEditor.Surface
}
// Start moving selected nodes
StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location;
_movingNodesDelta = Float2.Zero;
OnGetNodesToMove();
if (!Root.GetKey(KeyboardKeys.Shift))
{
StartMouseCapture();
_movingSelectionViewPos = _rootControl.Location;
_movingNodesDelta = Float2.Zero;
OnGetNodesToMove();
}
Focus();
return true;
}
@@ -515,7 +556,12 @@ namespace FlaxEditor.Surface
{
// Start selecting or commenting
StartMouseCapture();
ClearSelection();
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
{
ClearSelection();
}
Focus();
return true;
}

View File

@@ -405,6 +405,15 @@ namespace FlaxEditor.Surface
new InputActionsContainer.Binding(options => options.Paste, Paste),
new InputActionsContainer.Binding(options => options.Cut, Cut),
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }),
new InputActionsContainer.Binding(options => options.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }),
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }),
new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }),
new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }),
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
});
Context.ControlSpawned += OnSurfaceControlSpawned;
@@ -710,6 +719,18 @@ namespace FlaxEditor.Surface
SelectionChanged?.Invoke();
}
/// <summary>
/// Removes the specified control from the selection.
/// </summary>
/// <param name="control">The control.</param>
public void RemoveFromSelection(SurfaceControl control)
{
if (!control.IsSelected)
return;
control.IsSelected = false;
SelectionChanged?.Invoke();
}
/// <summary>
/// Selects the specified control.
/// </summary>

View File

@@ -1518,6 +1518,7 @@ namespace FlaxEditor.Utilities
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
inputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
}
internal static string ToPathProject(string path)

View File

@@ -236,6 +236,7 @@ namespace FlaxEditor.Windows.Assets
var group = layout.Group("General");
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
minScreenSize.ValueBox.SlideSpeed = 0.005f;
minScreenSize.ValueBox.MinValue = 0.0f;
minScreenSize.ValueBox.MaxValue = 1.0f;
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
@@ -476,12 +477,12 @@ namespace FlaxEditor.Windows.Assets
}
}
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")]
[Tooltip("Level Of Detail index to preview UVs layout.")]
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs, 0.01f), VisibleIf("ShowUVs")]
[Tooltip("Level Of Detail index to preview UVs layout at.")]
public int LOD = 0;
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")]
[Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")]
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000, 0.01f), VisibleIf("ShowUVs")]
[Tooltip("Mesh index to show UVs layout for. Use -1 to display all UVs of all meshes")]
public int Mesh = -1;
private bool ShowUVs => _uvChannel != UVChannel.None;

View File

@@ -81,6 +81,7 @@ namespace FlaxEditor.Windows.Assets
}
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
resolution.ValueBox.SlideSpeed = 0.001f;
resolution.ValueBox.MinValue = 0.0001f;
resolution.ValueBox.MaxValue = 100.0f;
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using FlaxEditor.Content;
using FlaxEditor.CustomEditors;
using FlaxEditor.GUI;
using FlaxEditor.Scripting;
using FlaxEditor.Surface;
using FlaxEditor.Viewport.Previews;
@@ -114,6 +115,7 @@ namespace FlaxEditor.Windows.Assets
private readonly PropertiesProxy _properties;
private Tab _previewTab;
private ToolStripButton _showSourceCodeButton;
/// <inheritdoc />
public ParticleEmitterWindow(Editor editor, AssetItem item)
@@ -146,7 +148,8 @@ namespace FlaxEditor.Windows.Assets
// Toolstrip
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
_showSourceCodeButton = _toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode);
_showSourceCodeButton.LinkTooltip("Show generated shader source code");
_toolstrip.AddSeparator();
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
}
@@ -285,5 +288,15 @@ namespace FlaxEditor.Windows.Assets
/// <inheritdoc />
public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter;
/// <inheritdoc />
public override void Update(float deltaTime)
{
base.Update(deltaTime);
if (_asset == null)
return;
_showSourceCodeButton.Enabled = _asset.HasShaderCode;
}
}
}

View File

@@ -318,7 +318,7 @@ namespace FlaxEditor.Windows
private Color _colorWarning;
private Color _colorError;
private bool _colorDebugLogText;
/// <summary>
/// Initializes a new instance of the <see cref="DebugLogWindow"/> class.
/// </summary>
@@ -352,24 +352,12 @@ namespace FlaxEditor.Windows
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
toolstrip.AddSeparator();
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () =>
{
UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked);
editor.Options.Options.Interface.DebugLogShowErrorMessages = _groupButtons[0].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () =>
{
UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked);
editor.Options.Options.Interface.DebugLogShowWarningMessages = _groupButtons[1].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () =>
{
UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked);
editor.Options.Options.Interface.DebugLogShowInfoMessages = _groupButtons[2].Checked;
editor.Options.Apply(editor.Options.Options);
}).SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => { OnGroupButtonPressed(0); })
.SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => { OnGroupButtonPressed(1); })
.SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => { OnGroupButtonPressed(2); })
.SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
UpdateCount();
// Split panel
@@ -418,6 +406,27 @@ namespace FlaxEditor.Windows
OnEditorOptionsChanged(Editor.Options.Options);
}
private void OnGroupButtonPressed(int index)
{
UpdateLogTypeVisibility((LogGroup)index, _groupButtons[index].Checked);
if (Input.GetKey(KeyboardKeys.Shift))
{
for (int i = 0; i < (int)LogGroup.Max; i++)
{
if (i == index)
continue;
_groupButtons[i].Checked = !_groupButtons[index].Checked;
UpdateLogTypeVisibility((LogGroup)i, _groupButtons[i].Checked);
}
}
var options = Editor.Options.Options.Interface;
options.DebugLogShowErrorMessages = _groupButtons[0].Checked;
options.DebugLogShowWarningMessages = _groupButtons[1].Checked;
options.DebugLogShowInfoMessages = _groupButtons[2].Checked;
Editor.Options.Apply(Editor.Options.Options);
}
private void OnEditorOptionsChanged(EditorOptions options)
{
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
@@ -455,15 +464,9 @@ namespace FlaxEditor.Windows
// Create new entry
switch (_timestampsFormats)
{
case InterfaceOptions.TimestampsFormats.Utc:
desc.Title = $"[{DateTime.UtcNow}] {desc.Title}";
break;
case InterfaceOptions.TimestampsFormats.LocalTime:
desc.Title = $"[{DateTime.Now}] {desc.Title}";
break;
case InterfaceOptions.TimestampsFormats.TimeSinceStartup:
desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title;
break;
case InterfaceOptions.TimestampsFormats.Utc: desc.Title = $"[{DateTime.UtcNow}] {desc.Title}"; break;
case InterfaceOptions.TimestampsFormats.LocalTime: desc.Title = $"[{DateTime.Now}] {desc.Title}"; break;
case InterfaceOptions.TimestampsFormats.TimeSinceStartup: desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title; break;
}
var newEntry = new LogEntry(this, ref desc);

View File

@@ -405,6 +405,7 @@ namespace FlaxEditor.Windows
return;
Editor.Instance.SceneEditing.Delete();
});
InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
}
private void ChangeViewportRatio(ViewportScaleOptions v)

View File

@@ -830,6 +830,15 @@ namespace FlaxEditor.Windows
OnOutputTextChanged();
}
/// <summary>
/// Focus the debug command line and ensure that the output log window is visible.
/// </summary>
public void FocusCommand()
{
FocusOrShow();
_commandLineBox.Focus();
}
/// <inheritdoc />
public override void Update(float deltaTime)
{

View File

@@ -9,6 +9,10 @@
#include "Engine/Level/Types.h"
#include "Engine/Debug/Exceptions/JsonParseException.h"
#include "Engine/Profiler/ProfilerCPU.h"
#if USE_EDITOR
#include "Engine/Core/Collections/HashSet.h"
#include "Engine/Core/Collections/Dictionary.h"
#endif
#include <ThirdParty/rapidjson/document.h>
bool JsonStorageProxy::IsValidExtension(const StringView& extension)
@@ -56,27 +60,31 @@ bool JsonStorageProxy::GetAssetInfo(const StringView& path, Guid& resultId, Stri
#if USE_EDITOR
void ChangeIds(rapidjson_flax::Value& obj, rapidjson_flax::Document& document, const StringAnsi& srcId, const StringAnsi& dstId)
void FindObjectIds(const rapidjson_flax::Value& obj, const rapidjson_flax::Document& document, HashSet<Guid>& ids, const char* parentName = nullptr)
{
if (obj.IsObject())
{
for (rapidjson_flax::Value::MemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i)
for (rapidjson_flax::Value::ConstMemberIterator i = obj.MemberBegin(); i != obj.MemberEnd(); ++i)
{
ChangeIds(i->value, document, srcId, dstId);
FindObjectIds(i->value, document, ids, i->name.GetString());
}
}
else if (obj.IsArray())
{
for (rapidjson::SizeType i = 0; i < obj.Size(); i++)
{
ChangeIds(obj[i], document, srcId, dstId);
FindObjectIds(obj[i], document, ids, parentName);
}
}
else if (obj.IsString())
else if (obj.IsString() && obj.GetStringLength() == 32)
{
if (StringUtils::Compare(srcId.Get(), obj.GetString()) == 0)
if (parentName && StringUtils::Compare(parentName, "ID") == 0)
{
obj.SetString(dstId.Get(), document.GetAllocator());
auto value = JsonTools::GetGuid(obj);
if (value.IsValid())
{
ids.Add(value);
}
}
}
}
@@ -91,9 +99,7 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId)
// Load file
Array<byte> fileData;
if (File::ReadAllBytes(path, fileData))
{
return false;
}
// Parse data
rapidjson_flax::Document document;
@@ -107,33 +113,35 @@ bool JsonStorageProxy::ChangeId(const StringView& path, const Guid& newId)
return false;
}
// Try get asset metadata
// Get all IDs inside the file
HashSet<Guid> ids;
FindObjectIds(document, document, ids);
// Remap into a unique IDs
Dictionary<Guid, Guid> remap;
remap.EnsureCapacity(ids.Count());
for (const auto& id : ids)
remap.Add(id.Item, Guid::New());
// Remap asset ID using the provided value
auto idNode = document.FindMember("ID");
if (idNode == document.MemberEnd())
{
return true;
}
remap[JsonTools::GetGuid(idNode->value)] = newId;
// Change IDs
auto oldIdStr = idNode->value.GetString();
auto newIdStr = newId.ToString(Guid::FormatType::N).ToStringAnsi();
ChangeIds(document, document, oldIdStr, newIdStr);
// Change IDs of asset and objects inside asset
JsonTools::ChangeIds(document, remap);
// Save to file
rapidjson_flax::StringBuffer buffer;
PrettyJsonWriter writer(buffer);
document.Accept(writer.GetWriter());
if (File::WriteAllBytes(path, (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{
return true;
}
return false;
#else
LOG(Warning, "Editing cooked content is invalid.");
return true;
#endif
}

View File

@@ -18,6 +18,7 @@
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Engine/Platform/MessageBox.h"
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
{
@@ -118,6 +119,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
}
#else
#define HANDLE_VORBIS(chunkIndex, dataPtr, dataSize) \
MessageBox::Show(TEXT("Vorbis format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Vorbis format is not supported."); \
return CreateAssetResult::Error;
#endif
@@ -140,6 +142,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
break; \
default: \
{ \
MessageBox::Show(TEXT("Unknown audio format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning); \
LOG(Warning, "Unknown audio format."); \
return CreateAssetResult::Error; \
} \

View File

@@ -12,6 +12,7 @@
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/MessageBox.h"
#define USE_MIKKTSPACE 1
#include "ThirdParty/MikkTSpace/mikktspace.h"
#if USE_ASSIMP
@@ -181,6 +182,7 @@ bool MeshData::GenerateLightmapUVs()
for (int32 i = 0; i < (int32)vb.size(); i++)
lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv;
#else
MessageBox::Show(TEXT("Model lightmap UVs generation is not supported on this platform."), TEXT("Import error"), MessageBoxButtons::OK, MessageBoxIcon::Error);
LOG(Error, "Model lightmap UVs generation is not supported on this platform.");
#endif

View File

@@ -110,7 +110,7 @@ public:
/// <summary>
/// The animation update delta timescale. Can be used to speed up animation playback or create slow motion effect.
/// </summary>
API_FIELD(Attributes="EditorOrder(45), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(45), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
float UpdateSpeed = 1.0f;
/// <summary>
@@ -122,7 +122,7 @@ public:
/// <summary>
/// The master scale parameter for the actor bounding box. Helps to reduce mesh flickering effect on screen edges.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0), EditorDisplay(\"Skinned Model\")")
API_FIELD(Attributes="EditorOrder(60), DefaultValue(1.5f), Limit(0, float.MaxValue, 0.025f), EditorDisplay(\"Skinned Model\")")
float BoundsScale = 1.5f;
/// <summary>

View File

@@ -440,4 +440,18 @@ bool ParticleEmitter::Save(const StringView& path)
return SaveSurface(data);
}
bool ParticleEmitter::HasShaderCode() const
{
if (SimulationMode != ParticlesSimulationMode::GPU)
return false;
#if COMPILE_WITH_PARTICLE_GPU_GRAPH && COMPILE_WITH_SHADER_COMPILER
if (_shaderHeader.ParticleEmitter.GraphVersion == PARTICLE_GPU_GRAPH_VERSION
&& HasChunk(SHADER_FILE_CHUNK_SOURCE)
&& !HasDependenciesModified())
return true;
#endif
return false;
}
#endif

View File

@@ -173,6 +173,11 @@ public:
#if USE_EDITOR
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
bool Save(const StringView& path = StringView::Empty) override;
/// <summary>
/// Checks if the particle emitter has valid shader code present.
/// </summary>
API_PROPERTY() bool HasShaderCode() const;
#endif
protected:

View File

@@ -162,7 +162,7 @@ void BoxCollider::UpdateBounds()
void BoxCollider::GetGeometry(CollisionShape& collision)
{
Float3 size = _size * _cachedScale;
Float3 size = _size * _transform.Scale;
const float minSize = 0.001f;
size = Float3::Max(size.GetAbsolute() * 0.5f, Float3(minSize));
collision.SetBox(size.Raw);

View File

@@ -43,10 +43,9 @@ void CapsuleCollider::DrawPhysicsDebug(RenderView& view)
return;
Quaternion rotation;
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
if (view.Mode == ViewMode::PhysicsColliders && !GetIsTrigger())
DEBUG_DRAW_CAPSULE(_transform.LocalToWorld(_center), rotation, radius, height, _staticActor ? Color::CornflowerBlue : Color::Orchid, 0, true);
else
@@ -57,10 +56,9 @@ void CapsuleCollider::OnDebugDrawSelected()
{
Quaternion rotation;
Quaternion::Multiply(_transform.Orientation, Quaternion::Euler(0, 90, 0), rotation);
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
const Vector3 position = _transform.LocalToWorld(_center);
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, radius, height, Color::GreenYellow, 0, false);
@@ -92,9 +90,8 @@ void CapsuleCollider::UpdateBounds()
void CapsuleCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float minSize = 0.001f;
const float radius = Math::Max(Math::Abs(_radius) * scaling, minSize);
const float height = Math::Max(Math::Abs(_height) * scaling, minSize);
const float radius = Math::Max(Math::Abs(_radius) * _cachedScale, minSize);
const float height = Math::Max(Math::Abs(_height) * _cachedScale, minSize);
collision.SetCapsule(radius, height * 0.5f);
}

View File

@@ -21,6 +21,7 @@ CharacterController::CharacterController(const SpawnParams& params)
, _upDirection(Vector3::Up)
, _gravityDisplacement(Vector3::Zero)
, _nonWalkableMode(NonWalkableModes::PreventClimbing)
, _originMode(OriginModes::CapsuleCenter)
, _lastFlags(CollisionFlags::None)
{
_contactOffset = 10.0f;
@@ -35,9 +36,7 @@ void CharacterController::SetRadius(const float value)
{
if (value == _radius)
return;
_radius = value;
UpdateSize();
UpdateBounds();
}
@@ -51,9 +50,7 @@ void CharacterController::SetHeight(const float value)
{
if (value == _height)
return;
_height = value;
UpdateSize();
UpdateBounds();
}
@@ -87,6 +84,23 @@ void CharacterController::SetNonWalkableMode(NonWalkableModes value)
PhysicsBackend::SetControllerNonWalkableMode(_controller, (int32)value);
}
CharacterController::OriginModes CharacterController::GetOriginMode() const
{
return _originMode;
}
void CharacterController::SetOriginMode(OriginModes value)
{
if (_originMode == value)
return;
_originMode = value;
if (_controller)
{
DeleteController();
CreateController();
}
}
float CharacterController::GetStepOffset() const
{
return _stepOffset;
@@ -96,15 +110,11 @@ void CharacterController::SetStepOffset(float value)
{
if (value == _stepOffset)
return;
_stepOffset = value;
if (_controller)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float contactOffset = Math::Max(_contactOffset, ZeroTolerance);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const float radius = Math::Max(Math::Abs(_radius) * scaling - contactOffset, CC_MIN_SIZE);
float height, radius;
GetControllerSize(height, radius);
PhysicsBackend::SetControllerStepOffset(_controller, Math::Min(value, height + radius * 2.0f - CC_MIN_SIZE));
}
}
@@ -169,17 +179,69 @@ CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector
CharacterController::CollisionFlags CharacterController::Move(const Vector3& displacement)
{
CollisionFlags result = CollisionFlags::None;
if (_controller)
if (_controller && !_isUpdatingTransform)
{
// Perform move
const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds();
result = (CollisionFlags)PhysicsBackend::MoveController(_controller, _shape, displacement, _minMoveDistance, deltaTime);
_lastFlags = result;
Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
// Update position
Vector3 position;
if (_originMode == OriginModes::Base)
position = PhysicsBackend::GetControllerBasePosition(_controller);
else
position = PhysicsBackend::GetControllerPosition(_controller);
position -= _center;
_isUpdatingTransform = true;
SetPosition(position);
_isUpdatingTransform = false;
}
return result;
}
void CharacterController::Resize(float height, float radius)
{
const float heightDiff = height - _height;
const float radiusDiff = radius - _radius;
if (Math::IsZero(heightDiff) && Math::IsZero(radiusDiff))
return;
_height = height;
_radius = radius;
if (_controller)
{
float centerDiff = heightDiff * 0.5f + radiusDiff;
// Change physics size
GetControllerSize(height, radius);
PhysicsBackend::SetControllerSize(_controller, radius, height);
Vector3 positionDelta = _upDirection * centerDiff;
// Change physics position to maintain feet placement (base)
Vector3 position;
switch (_originMode)
{
case OriginModes::CapsuleCenter:
position = PhysicsBackend::GetControllerPosition(_controller);
position += positionDelta;
_center += positionDelta;
PhysicsBackend::SetControllerPosition(_controller, position);
break;
case OriginModes::Base:
position = PhysicsBackend::GetControllerBasePosition(_controller);
position += positionDelta;
PhysicsBackend::SetControllerBasePosition(_controller, position);
break;
}
// Change actor position
_isUpdatingTransform = true;
SetPosition(position - _center);
_isUpdatingTransform = false;
}
UpdateBounds();
}
#if USE_EDITOR
#include "Engine/Debug/DebugDraw.h"
@@ -187,23 +249,38 @@ CharacterController::CollisionFlags CharacterController::Move(const Vector3& dis
void CharacterController::DrawPhysicsDebug(RenderView& view)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
Quaternion rotation = Quaternion::Euler(90, 0, 0);
const Vector3 position = GetControllerPosition();
if (view.Mode == ViewMode::PhysicsColliders)
DEBUG_DRAW_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::LightYellow, 0, true);
DEBUG_DRAW_CAPSULE(position, rotation, _radius, _height, Color::LightYellow, 0, true);
else
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow * 0.8f, 0, true);
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow * 0.8f, 0, true);
}
void CharacterController::OnDebugDrawSelected()
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
DEBUG_DRAW_WIRE_CAPSULE(position, Quaternion::Euler(90, 0, 0), radius, height, Color::GreenYellow, 0, false);
Quaternion rotation = Quaternion::Euler(90, 0, 0);
const Vector3 position = GetControllerPosition();
DEBUG_DRAW_WIRE_CAPSULE(position, rotation, _radius, _height, Color::GreenYellow, 0, false);
if (_controller)
{
// Physics backend capsule shape
float height, radius;
GetControllerSize(height, radius);
Vector3 pos = PhysicsBackend::GetControllerPosition(_controller);
DEBUG_DRAW_WIRE_CAPSULE(pos, rotation, radius, height, Color::Blue.AlphaMultiplied(0.2f), 0, false);
#if 0
// More technical visuals debugging
Vector3 base = PhysicsBackend::GetControllerBasePosition(_controller);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(base, 5.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos, 4.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), 2.0f), Color::Red, 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos + Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(pos - Vector3(0, height * 0.5f, 0), radius), Color::Red.AlphaMultiplied(0.5f), 0, false);
DEBUG_DRAW_WIRE_CYLINDER(pos, Quaternion::Identity, radius, height, Color::Red.AlphaMultiplied(0.2f), 0, false);
#endif
}
// Base
Collider::OnDebugDrawSelected();
@@ -215,10 +292,14 @@ void CharacterController::CreateController()
{
// Create controller
ASSERT(_controller == nullptr && _shape == nullptr);
_cachedScale = GetScale();
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const Vector3 position = _transform.LocalToWorld(_center);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, Math::Abs(_radius) * scaling, Math::Abs(_height) * scaling, _stepOffset, _shape);
_cachedScale = GetScale().GetAbsolute().MaxValue();
float height, radius;
GetControllerSize(height, radius);
Vector3 position = _center;
if (_originMode == OriginModes::Base)
position += _upDirection * (_height * 0.5f + _radius);
position = _transform.LocalToWorld(position);
_controller = PhysicsBackend::CreateController(GetPhysicsScene()->GetPhysicsScene(), this, this, _contactOffset, position, _slopeLimit, (int32)_nonWalkableMode, Material, radius, height, _stepOffset, _shape);
// Setup
PhysicsBackend::SetControllerUpDirection(_controller, _upDirection);
@@ -241,13 +322,35 @@ void CharacterController::UpdateSize() const
{
if (_controller)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling - Math::Max(_contactOffset, ZeroTolerance), CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
float height, radius;
GetControllerSize(height, radius);
PhysicsBackend::SetControllerSize(_controller, radius, height);
}
}
Vector3 CharacterController::GetControllerPosition() const
{
Vector3 position = _center;
if (_originMode == OriginModes::Base)
position += _upDirection * (_height * 0.5f + _radius);
position = _transform.LocalToWorld(position);
return position;
}
void CharacterController::GetControllerSize(float& height, float& radius) const
{
// Use absolute values including scale
height = Math::Abs(_height) * _cachedScale;
radius = Math::Abs(_radius) * _cachedScale;
// Exclude contact offset around the capsule (otherwise character floats in the air)
radius = radius - Math::Max(_contactOffset, 0.0f);
// Prevent too small controllers
height = Math::Max(height, CC_MIN_SIZE);
radius = Math::Max(radius, CC_MIN_SIZE);
}
void CharacterController::CreateShape()
{
// Not used
@@ -255,10 +358,10 @@ void CharacterController::CreateShape()
void CharacterController::UpdateBounds()
{
const float scaling = GetScale().GetAbsolute().MaxValue();
const float radius = Math::Max(Math::Abs(_radius) * scaling, CC_MIN_SIZE);
const float height = Math::Max(Math::Abs(_height) * scaling, CC_MIN_SIZE);
const Vector3 position = _transform.LocalToWorld(_center);
_cachedScale = GetScale().GetAbsolute().MaxValue();
float height, radius;
GetControllerSize(height, radius);
const Vector3 position = GetControllerPosition();
const Vector3 extent(radius, height * 0.5f + radius, radius);
_box = BoundingBox(position - extent, position + extent);
BoundingSphere::FromBox(_box, _sphere);
@@ -292,6 +395,21 @@ RigidBody* CharacterController::GetAttachedRigidBody() const
return nullptr;
}
void CharacterController::SetCenter(const Vector3& value)
{
if (value == _center)
return;
Vector3 delta = value - _center;
_center = value;
if (_controller)
{
// Change physics position while maintaining actor placement
Vector3 position = PhysicsBackend::GetControllerPosition(_controller);
position += _upDirection * delta;
PhysicsBackend::SetControllerPosition(_controller, position);
}
}
void CharacterController::OnActiveTransformChanged()
{
if (!_shape)
@@ -300,7 +418,12 @@ void CharacterController::OnActiveTransformChanged()
// Change actor transform (but with locking)
ASSERT(!_isUpdatingTransform);
_isUpdatingTransform = true;
const Vector3 position = PhysicsBackend::GetControllerPosition(_controller) - _center;
Vector3 position;
if (_originMode == OriginModes::Base)
position = PhysicsBackend::GetControllerBasePosition(_controller);
else
position = PhysicsBackend::GetControllerPosition(_controller);
position -= _center;
SetPosition(position);
_isUpdatingTransform = false;
@@ -319,7 +442,7 @@ void CharacterController::UpdateGeometry()
return;
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
UpdateSize();
}
@@ -382,8 +505,11 @@ void CharacterController::OnTransformChanged()
const Vector3 position = _transform.LocalToWorld(_center);
if (!_isUpdatingTransform && _controller)
{
PhysicsBackend::SetControllerPosition(_controller, position);
const Float3 scale = GetScale();
if (_originMode == OriginModes::Base)
PhysicsBackend::SetControllerBasePosition(_controller, position);
else
PhysicsBackend::SetControllerPosition(_controller, position);
const float scale = GetScale().GetAbsolute().MaxValue();
if (_cachedScale != scale)
UpdateGeometry();
UpdateBounds();

View File

@@ -41,6 +41,22 @@ public:
Below = 1 << 2,
};
/// <summary>
/// Specifies how a character controller capsule placement.
/// </summary>
API_ENUM() enum class OriginModes
{
/// <summary>
/// Character origin starts at capsule center (including Center offset properly).
/// </summary>
CapsuleCenter,
/// <summary>
/// Character origin starts at capsule base position aka character feet placement.
/// </summary>
Base,
};
/// <summary>
/// Specifies how a character controller interacts with non-walkable parts.
/// </summary>
@@ -69,6 +85,7 @@ private:
Vector3 _upDirection;
Vector3 _gravityDisplacement;
NonWalkableModes _nonWalkableMode;
OriginModes _originMode;
CollisionFlags _lastFlags;
public:
@@ -84,13 +101,13 @@ public:
API_PROPERTY() void SetRadius(float value);
/// <summary>
/// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
/// Gets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\"), ValueCategory(Utils.ValueCategory.Distance)")
float GetHeight() const;
/// <summary>
/// Sets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale.
/// Sets the height of the capsule as a distance between the two sphere centers at the end of the capsule. The capsule height is measured in the object's local space and will be scaled by the actor's world scale.
/// </summary>
API_PROPERTY() void SetHeight(float value);
@@ -116,6 +133,17 @@ public:
/// </summary>
API_PROPERTY() void SetNonWalkableMode(NonWalkableModes value);
/// <summary>
/// Gets the position origin placement mode.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(216), DefaultValue(OriginModes.CapsuleCenter), EditorDisplay(\"Character Controller\")")
OriginModes GetOriginMode() const;
/// <summary>
/// Sets the position origin placement mode.
/// </summary>
API_PROPERTY() void SetOriginMode(OriginModes value);
/// <summary>
/// Gets the step height. The character will step up a stair only if it is closer to the ground than the indicated value. This should not be greater than the Character Controllers height or it will generate an error.
/// </summary>
@@ -194,6 +222,13 @@ public:
/// <returns>The collision flags. It can be used to trigger various character animations.</returns>
API_FUNCTION() CollisionFlags Move(const Vector3& displacement);
/// <summary>
/// Updates the character height and center position to ensure its feet position stays the same. This can be used to implement a 'crouch' functionality for example. Maintains the same actor position to stay in the middle of capsule by adjusting center of collider accordingly to height difference.
/// </summary>
/// <param name="height">The height of the capsule, measured in the object's local space.</param>
/// <param name="radius">The radius of the capsule, measured in the object's local space.</param>
API_FUNCTION() void Resize(float height, float radius);
protected:
/// <summary>
/// Creates the physics actor.
@@ -210,6 +245,10 @@ protected:
/// </summary>
void UpdateSize() const;
private:
Vector3 GetControllerPosition() const;
void GetControllerSize(float& height, float& radius) const;
public:
// [Collider]
#if USE_EDITOR
@@ -220,6 +259,7 @@ public:
void AddMovement(const Vector3& translation, const Quaternion& rotation) override;
bool CanAttach(RigidBody* rigidBody) const override;
RigidBody* GetAttachedRigidBody() const override;
void SetCenter(const Vector3& value) override;
// [IPhysicsActor]
void OnActiveTransformChanged() override;

View File

@@ -205,7 +205,7 @@ void Collider::CreateShape()
ASSERT(_shape == nullptr);
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
CollisionShape shape;
GetGeometry(shape);
@@ -222,7 +222,7 @@ void Collider::UpdateGeometry()
return;
// Setup shape geometry
_cachedScale = GetScale();
_cachedScale = GetScale().GetAbsolute().MaxValue();
CollisionShape shape;
GetGeometry(shape);
@@ -427,7 +427,7 @@ void Collider::OnTransformChanged()
}
}
const Float3 scale = GetScale();
const float scale = GetScale().GetAbsolute().MaxValue();
if (_cachedScale != scale)
UpdateGeometry();
UpdateBounds();

View File

@@ -24,7 +24,7 @@ protected:
bool _isTrigger;
void* _shape;
void* _staticActor;
Float3 _cachedScale;
float _cachedScale;
float _contactOffset;
Vector3 _cachedLocalPosePos;
Quaternion _cachedLocalPoseRot;
@@ -61,7 +61,7 @@ public:
/// <summary>
/// Sets the center of the collider, measured in the object's local space.
/// </summary>
API_PROPERTY() void SetCenter(const Vector3& value);
API_PROPERTY() virtual void SetCenter(const Vector3& value);
/// <summary>
/// Gets the contact offset. Colliders whose distance is less than the sum of their ContactOffset values will generate contacts. The contact offset must be positive. Contact offset allows the collision detection system to predictively enforce the contact constraint even when the objects are slightly separated.

View File

@@ -131,7 +131,7 @@ void MeshCollider::UpdateBounds()
void MeshCollider::GetGeometry(CollisionShape& collision)
{
// Prepare scale
Float3 scale = _cachedScale;
Float3 scale = _transform.Scale;
const float minSize = 0.001f;
Float3 scaleAbs = scale.GetAbsolute();
if (scaleAbs.X < minSize)

View File

@@ -67,8 +67,7 @@ void SphereCollider::UpdateBounds()
void SphereCollider::GetGeometry(CollisionShape& collision)
{
const float scaling = _cachedScale.GetAbsolute().MaxValue();
const float radius = Math::Abs(_radius) * scaling;
const float radius = Math::Abs(_radius) * _cachedScale;
const float minSize = 0.001f;
collision.SetSphere(Math::Max(radius, minSize));
}

View File

@@ -259,8 +259,7 @@ void SplineCollider::GetGeometry(CollisionShape& collision)
}
// Prepare scale
Float3 scale = _cachedScale;
scale = Float3::Max(scale.GetAbsolute(), minSize);
Float3 scale = Float3::Max(_transform.Scale.GetAbsolute(), minSize);
// TODO: add support for cooking collision for static splines in editor and reusing it in game

View File

@@ -3155,10 +3155,9 @@ void* PhysicsBackend::CreateController(void* scene, IPhysicsActor* actor, Physic
desc.material = (PxMaterial*)((PhysicalMaterial*)material->Instance)->GetPhysicsMaterial();
else
desc.material = DefaultMaterial;
const float minSize = 0.001f;
desc.height = Math::Max(height, minSize);
desc.radius = Math::Max(radius - Math::Max(contactOffset, 0.0f), minSize);
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - minSize);
desc.height = height;
desc.radius = radius;
desc.stepOffset = Math::Min(stepOffset, desc.height + desc.radius * 2.0f - 0.001f);
auto controllerPhysX = (PxCapsuleController*)scenePhysX->ControllerManager->createController(desc);
PxRigidActor* actorPhysX = controllerPhysX->getActor();
ASSERT(actorPhysX && actorPhysX->getNbShapes() == 1);
@@ -3183,7 +3182,7 @@ void PhysicsBackend::SetControllerSize(void* controller, float radius, float hei
{
auto controllerPhysX = (PxCapsuleController*)controller;
controllerPhysX->setRadius(radius);
controllerPhysX->resize(height);
controllerPhysX->setHeight(height);
}
void PhysicsBackend::SetControllerSlopeLimit(void* controller, float value)
@@ -3204,6 +3203,20 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value)
controllerPhysX->setStepOffset(value);
}
Vector3 PhysicsBackend::GetControllerBasePosition(void* controller)
{
auto controllerPhysX = (PxCapsuleController*)controller;
const Vector3 origin = SceneOrigins[controllerPhysX->getScene()];
return P2C(controllerPhysX->getFootPosition()) + origin;
}
void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value)
{
auto controllerPhysX = (PxCapsuleController*)controller;
const Vector3 sceneOrigin = SceneOrigins[controllerPhysX->getScene()];
controllerPhysX->setFootPosition(PxExtendedVec3(value.X - sceneOrigin.X, value.Y - sceneOrigin.Y, value.Z - sceneOrigin.Z));
}
Vector3 PhysicsBackend::GetControllerUpDirection(void* controller)
{
auto controllerPhysX = (PxCapsuleController*)controller;

View File

@@ -248,6 +248,8 @@ public:
static void SetControllerSlopeLimit(void* controller, float value);
static void SetControllerNonWalkableMode(void* controller, int32 value);
static void SetControllerStepOffset(void* controller, float value);
static Vector3 GetControllerBasePosition(void* controller);
static void SetControllerBasePosition(void* controller, const Vector3& value);
static Vector3 GetControllerUpDirection(void* controller);
static void SetControllerUpDirection(void* controller, const Vector3& value);
static Vector3 GetControllerPosition(void* controller);

View File

@@ -43,17 +43,17 @@ void* WindowsPlatform::Instance = nullptr;
extern "C" {
static HANDLE dbgHelpLock;
void DbgHelpInit()
void FlaxDbgHelpInit()
{
dbgHelpLock = CreateMutexW(nullptr, FALSE, nullptr);
}
void DbgHelpLock()
void FlaxDbgHelpLock()
{
WaitForSingleObject(dbgHelpLock, INFINITE);
}
void DbgHelpUnlock()
void FlaxDbgHelpUnlock()
{
ReleaseMutex(dbgHelpLock);
}
@@ -552,7 +552,7 @@ void WindowsPlatform::PreInit(void* hInstance)
#if CRASH_LOG_ENABLE
TCHAR buffer[MAX_PATH] = { 0 };
DbgHelpLock();
FlaxDbgHelpLock();
if (::GetModuleFileNameW(::GetModuleHandleW(nullptr), buffer, MAX_PATH))
SymbolsPath.Add(StringUtils::GetDirectoryName(buffer));
if (::GetEnvironmentVariableW(TEXT("_NT_SYMBOL_PATH"), buffer, MAX_PATH))
@@ -561,7 +561,7 @@ void WindowsPlatform::PreInit(void* hInstance)
options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS;
SymSetOptions(options);
OnSymbolsPathModified();
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild);
@@ -781,7 +781,7 @@ void WindowsPlatform::BeforeExit()
void WindowsPlatform::Exit()
{
#if CRASH_LOG_ENABLE
DbgHelpLock();
FlaxDbgHelpLock();
#if !TRACY_ENABLE
if (SymInitialized)
{
@@ -790,7 +790,7 @@ void WindowsPlatform::Exit()
}
#endif
SymbolsPath.Resize(0);
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
#if !PLATFORM_SDL
@@ -1298,14 +1298,14 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
#if CRASH_LOG_ENABLE
// Refresh modules info during next stack trace collecting to have valid debug symbols information
DbgHelpLock();
FlaxDbgHelpLock();
if (folder.HasChars() && !SymbolsPath.Contains(folder))
{
SymbolsPath.Add(folder);
SymbolsPath.Last().Replace('/', '\\');
OnSymbolsPathModified();
}
DbgHelpUnlock();
FlaxDbgHelpUnlock();
#endif
return handle;
@@ -1316,7 +1316,7 @@ void* WindowsPlatform::LoadLibrary(const Char* filename)
Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
{
Array<StackFrame> result;
DbgHelpLock();
FlaxDbgHelpLock();
// Initialize
HANDLE process = GetCurrentProcess();
@@ -1448,7 +1448,7 @@ Array<PlatformBase::StackFrame> WindowsPlatform::GetStackFrames(int32 skipCount,
}
}
DbgHelpUnlock();
FlaxDbgHelpUnlock();
return result;
}

View File

@@ -13,6 +13,7 @@
#include "Engine/Graphics/Textures/TextureData.h"
#include "Engine/Graphics/PixelFormatExtensions.h"
#if USE_EDITOR
#include "Engine/Platform/MessageBox.h"
#include "Engine/Graphics/GPUDevice.h"
#endif
#include "Engine/Utilities/AnsiPathTempFile.h"
@@ -358,6 +359,9 @@ HRESULT LoadFromEXRFile(const StringView& path, DirectX::ScratchImage& image)
free(pixels);
return result;
#else
#if USE_EDITOR
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
#endif
LOG(Warning, "EXR format is not supported.");
return E_FAIL;
#endif

View File

@@ -13,6 +13,7 @@
#include "Engine/Graphics/PixelFormatExtensions.h"
#include "Engine/Utilities/AnsiPathTempFile.h"
#include "Engine/Platform/File.h"
#include "Engine/Platform/MessageBox.h"
#define STBI_ASSERT(x) ASSERT(x)
#define STBI_MALLOC(sz) Allocator::Allocate(sz)
@@ -286,21 +287,27 @@ bool TextureTool::ExportTextureStb(ImageType type, const StringView& path, const
break;
}
case ImageType::GIF:
MessageBox::Show(TEXT("GIF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "GIF format is not supported.");
break;
case ImageType::TIFF:
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "GIF format is not supported.");
break;
case ImageType::DDS:
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "DDS format is not supported.");
break;
case ImageType::RAW:
MessageBox::Show(TEXT("RAW format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "RAW format is not supported.");
break;
case ImageType::EXR:
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "EXR format is not supported.");
break;
default:
MessageBox::Show(TEXT("Unknown format."), TEXT("Export warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Unknown format.");
break;
}
@@ -434,17 +441,21 @@ bool TextureTool::ImportTextureStb(ImageType type, const StringView& path, Textu
free(pixels);
#else
MessageBox::Show(TEXT("EXR format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "EXR format is not supported.");
#endif
break;
}
case ImageType::DDS:
MessageBox::Show(TEXT("DDS format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "DDS format is not supported.");
break;
case ImageType::TIFF:
MessageBox::Show(TEXT("TIFF format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "TIFF format is not supported.");
break;
default:
MessageBox::Show(TEXT("Unknown format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
LOG(Warning, "Unknown format.");
return true;
}

View File

@@ -301,7 +301,7 @@ namespace FlaxEngine.GUI
else
{
text = _watermarkText;
if (text.Length > 0)
if (text?.Length > 0)
{
Render2D.DrawText(font, _watermarkText, WatermarkTextColor, ref _layout, TextMaterial);
}

View File

@@ -29,21 +29,24 @@
#include "client/tracy_rpmalloc.cpp"
#include "client/TracyAlloc.cpp"
#include "client/TracyOverride.cpp"
#include "client/TracyKCore.cpp"
#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
# include "libbacktrace/alloc.cpp"
# include "libbacktrace/dwarf.cpp"
# include "libbacktrace/fileline.cpp"
# include "libbacktrace/mmapio.cpp"
# include "libbacktrace/posix.cpp"
# include "libbacktrace/sort.cpp"
# include "libbacktrace/state.cpp"
# if TRACY_HAS_CALLSTACK == 4
# include "libbacktrace/macho.cpp"
# else
# include "libbacktrace/elf.cpp"
#if defined(TRACY_HAS_CALLSTACK)
# if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
# include "libbacktrace/alloc.cpp"
# include "libbacktrace/dwarf.cpp"
# include "libbacktrace/fileline.cpp"
# include "libbacktrace/mmapio.cpp"
# include "libbacktrace/posix.cpp"
# include "libbacktrace/sort.cpp"
# include "libbacktrace/state.cpp"
# if TRACY_HAS_CALLSTACK == 4
# include "libbacktrace/macho.cpp"
# else
# include "libbacktrace/elf.cpp"
# endif
# include "common/TracyStackFrames.cpp"
# endif
# include "common/TracyStackFrames.cpp"
#endif
#ifdef _MSC_VER

View File

@@ -305,6 +305,14 @@ static const char* DecodeIosDevice( const char* id )
"iPhone14,4", "iPhone 13 Mini",
"iPhone14,5", "iPhone 13",
"iPhone14,6", "iPhone SE 3rd Gen",
"iPhone14,7", "iPhone 14",
"iPhone14,8", "iPhone 14 Plus",
"iPhone15,2", "iPhone 14 Pro",
"iPhone15,3", "iPhone 14 Pro Max",
"iPhone15,4", "iPhone 15",
"iPhone15,5", "iPhone 15 Plus",
"iPhone16,1", "iPhone 15 Pro",
"iPhone16,2", "iPhone 15 Pro Max",
"iPad1,1", "iPad (A1219/A1337)",
"iPad2,1", "iPad 2 (A1395)",
"iPad2,2", "iPad 2 (A1396)",
@@ -365,6 +373,8 @@ static const char* DecodeIosDevice( const char* id )
"iPad11,4", "iPad Air 3rd gen (A2123/A2153/A2154)",
"iPad11,6", "iPad 8th gen (WiFi)",
"iPad11,7", "iPad 8th gen (WiFi+Cellular)",
"iPad12,1", "iPad 9th Gen (WiFi)",
"iPad12,2", "iPad 9th Gen (WiFi+Cellular)",
"iPad13,1", "iPad Air 4th gen (WiFi)",
"iPad13,2", "iPad Air 4th gen (WiFi+Cellular)",
"iPad13,4", "iPad Pro 11\" 3rd gen",
@@ -377,6 +387,14 @@ static const char* DecodeIosDevice( const char* id )
"iPad13,11", "iPad Pro 12.9\" 5th gen",
"iPad13,16", "iPad Air 5th Gen (WiFi)",
"iPad13,17", "iPad Air 5th Gen (WiFi+Cellular)",
"iPad13,18", "iPad 10th Gen",
"iPad13,19", "iPad 10th Gen",
"iPad14,1", "iPad mini 6th Gen (WiFi)",
"iPad14,2", "iPad mini 6th Gen (WiFi+Cellular)",
"iPad14,3", "iPad Pro 11\" 4th Gen",
"iPad14,4", "iPad Pro 11\" 4th Gen",
"iPad14,5", "iPad Pro 12.9\" 6th Gen",
"iPad14,6", "iPad Pro 12.9\" 6th Gen",
"iPod1,1", "iPod Touch",
"iPod2,1", "iPod Touch 2nd gen",
"iPod3,1", "iPod Touch 3rd gen",

View File

@@ -3,10 +3,12 @@
#include <stdio.h>
#include <string.h>
#include "TracyCallstack.hpp"
#include "TracyDebug.hpp"
#include "TracyFastVector.hpp"
#include "TracyStringHelpers.hpp"
#include "../common/TracyAlloc.hpp"
#include "TracyDebug.hpp"
#include "../common/TracySystem.hpp"
#ifdef TRACY_HAS_CALLSTACK
@@ -31,7 +33,6 @@
# include <dlfcn.h>
# include <cxxabi.h>
# include <stdlib.h>
# include "TracyFastVector.hpp"
#elif TRACY_HAS_CALLSTACK == 5
# include <dlfcn.h>
# include <cxxabi.h>
@@ -66,7 +67,7 @@ extern "C"
extern "C" const char* ___tracy_demangle( const char* mangled );
#ifndef TRACY_DEMANGLE
constexpr size_t ___tracy_demangle_buffer_len = 1024*1024;
constexpr size_t ___tracy_demangle_buffer_len = 1024*1024;
char* ___tracy_demangle_buffer;
void ___tracy_init_demangle_buffer()
@@ -90,9 +91,177 @@ extern "C" const char* ___tracy_demangle( const char* mangled )
#endif
#endif
#if TRACY_HAS_CALLSTACK == 3
# define TRACY_USE_IMAGE_CACHE
# include <link.h>
#endif
namespace tracy
{
#ifdef TRACY_USE_IMAGE_CACHE
// when we have access to dl_iterate_phdr(), we can build a cache of address ranges to image paths
// so we can quickly determine which image an address falls into.
// We refresh this cache only when we hit an address that doesn't fall into any known range.
class ImageCache
{
public:
struct ImageEntry
{
void* m_startAddress = nullptr;
void* m_endAddress = nullptr;
char* m_name = nullptr;
};
ImageCache()
: m_images( 512 )
{
Refresh();
}
~ImageCache()
{
Clear();
}
const ImageEntry* GetImageForAddress( void* address )
{
const ImageEntry* entry = GetImageForAddressImpl( address );
if( !entry )
{
Refresh();
return GetImageForAddressImpl( address );
}
return entry;
}
private:
tracy::FastVector<ImageEntry> m_images;
bool m_updated = false;
bool m_haveMainImageName = false;
static int Callback( struct dl_phdr_info* info, size_t size, void* data )
{
ImageCache* cache = reinterpret_cast<ImageCache*>( data );
const auto startAddress = reinterpret_cast<void*>( info->dlpi_addr );
if( cache->Contains( startAddress ) ) return 0;
const uint32_t headerCount = info->dlpi_phnum;
assert( headerCount > 0);
const auto endAddress = reinterpret_cast<void*>( info->dlpi_addr +
info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz);
ImageEntry* image = cache->m_images.push_next();
image->m_startAddress = startAddress;
image->m_endAddress = endAddress;
// the base executable name isn't provided when iterating with dl_iterate_phdr,
// we will have to patch the executable image name outside this callback
if( info->dlpi_name && info->dlpi_name[0] != '\0' )
{
size_t sz = strlen( info->dlpi_name ) + 1;
image->m_name = (char*)tracy_malloc( sz );
memcpy( image->m_name, info->dlpi_name, sz );
}
else
{
image->m_name = nullptr;
}
cache->m_updated = true;
return 0;
}
bool Contains( void* startAddress ) const
{
return std::any_of( m_images.begin(), m_images.end(), [startAddress]( const ImageEntry& entry ) { return startAddress == entry.m_startAddress; } );
}
void Refresh()
{
m_updated = false;
dl_iterate_phdr( Callback, this );
if( m_updated )
{
std::sort( m_images.begin(), m_images.end(),
[]( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.m_startAddress > rhs.m_startAddress; } );
// patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks
UpdateMainImageName();
}
}
void UpdateMainImageName()
{
if( m_haveMainImageName )
{
return;
}
for( ImageEntry& entry : m_images )
{
if( entry.m_name == nullptr )
{
Dl_info dlInfo;
if( dladdr( (void *)entry.m_startAddress, &dlInfo ) )
{
if( dlInfo.dli_fname )
{
size_t sz = strlen( dlInfo.dli_fname ) + 1;
entry.m_name = (char*)tracy_malloc( sz );
memcpy( entry.m_name, dlInfo.dli_fname, sz );
}
}
// we only expect one entry to be null for the main executable entry
break;
}
}
m_haveMainImageName = true;
}
const ImageEntry* GetImageForAddressImpl( void* address ) const
{
auto it = std::lower_bound( m_images.begin(), m_images.end(), address,
[]( const ImageEntry& lhs, const void* rhs ) { return lhs.m_startAddress > rhs; } );
if( it != m_images.end() && address < it->m_endAddress )
{
return it;
}
return nullptr;
}
void Clear()
{
for( ImageEntry& entry : m_images )
{
tracy_free( entry.m_name );
}
m_images.clear();
m_haveMainImageName = false;
}
};
#endif //#ifdef TRACY_USE_IMAGE_CACHE
// when "TRACY_SYMBOL_OFFLINE_RESOLVE" is set, instead of fully resolving symbols at runtime,
// simply resolve the offset and image name (which will be enough the resolving to be done offline)
#ifdef TRACY_SYMBOL_OFFLINE_RESOLVE
constexpr bool s_shouldResolveSymbolsOffline = true;
#else
static bool s_shouldResolveSymbolsOffline = false;
bool ShouldResolveSymbolsOffline()
{
const char* symbolOfflineResolve = GetEnvVar( "TRACY_SYMBOL_OFFLINE_RESOLVE" );
return (symbolOfflineResolve && symbolOfflineResolve[0] == '1');
}
#endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE
#if TRACY_HAS_CALLSTACK == 1
enum { MaxCbTrace = 64 };
@@ -108,13 +277,18 @@ extern "C"
typedef BOOL (__stdcall *t_SymFromInlineContext)( HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement, PSYMBOL_INFO Symbol );
typedef BOOL (__stdcall *t_SymGetLineFromInlineContext)( HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext, DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64 );
TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0;
t_SymAddrIncludeInlineTrace _SymAddrIncludeInlineTrace = 0;
t_SymQueryInlineTrace _SymQueryInlineTrace = 0;
t_SymFromInlineContext _SymFromInlineContext = 0;
t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0;
}
typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long );
___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChainPtr = nullptr;
TRACY_API unsigned long ___tracy_RtlWalkFrameChain( void** callers, unsigned long count, unsigned long flags)
{
return ___tracy_RtlWalkFrameChainPtr(callers, count, flags);
}
}
struct ModuleCache
{
@@ -136,18 +310,19 @@ struct KernelDriver
KernelDriver* s_krnlCache = nullptr;
size_t s_krnlCacheCnt;
void InitCallstackCritical()
{
___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" );
___tracy_RtlWalkFrameChainPtr = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" );
}
void InitCallstack()
void DbgHelpInit()
{
_SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymAddrIncludeInlineTrace" );
_SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymQueryInlineTrace" );
_SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymFromInlineContext" );
_SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress( GetModuleHandleA( "dbghelp.dll" ), "SymGetLineFromInlineContext" );
if( s_shouldResolveSymbolsOffline ) return;
_SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymAddrIncludeInlineTrace");
_SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymQueryInlineTrace");
_SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymFromInlineContext");
_SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymGetLineFromInlineContext");
#ifdef TRACY_DBGHELP_LOCK
DBGHELP_INIT;
@@ -157,9 +332,78 @@ void InitCallstack()
SymInitialize( GetCurrentProcess(), nullptr, true );
SymSetOptions( SYMOPT_LOAD_LINES );
#ifdef TRACY_DBGHELP_LOCK
DBGHELP_UNLOCK;
#endif
}
DWORD64 DbgHelpLoadSymbolsForModule( const char* imageName, uint64_t baseOfDll, uint32_t bllSize )
{
if( s_shouldResolveSymbolsOffline ) return 0;
return SymLoadModuleEx( GetCurrentProcess(), nullptr, imageName, nullptr, baseOfDll, bllSize, nullptr, 0 );
}
ModuleCache* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize )
{
DbgHelpLoadSymbolsForModule( imageName, baseOfDll, dllSize );
ModuleCache* cachedModule = s_modCache->push_next();
cachedModule->start = baseOfDll;
cachedModule->end = baseOfDll + dllSize;
// when doing offline symbol resolution, we must store the full path of the dll for the resolving to work
if( s_shouldResolveSymbolsOffline )
{
cachedModule->name = (char*)tracy_malloc_fast(imageNameLength + 1);
memcpy(cachedModule->name, imageName, imageNameLength);
cachedModule->name[imageNameLength] = '\0';
}
else
{
auto ptr = imageName + imageNameLength;
while (ptr > imageName && *ptr != '\\' && *ptr != '/') ptr--;
if (ptr > imageName) ptr++;
const auto namelen = imageName + imageNameLength - ptr;
cachedModule->name = (char*)tracy_malloc_fast(namelen + 3);
cachedModule->name[0] = '[';
memcpy(cachedModule->name + 1, ptr, namelen);
cachedModule->name[namelen + 1] = ']';
cachedModule->name[namelen + 2] = '\0';
}
return cachedModule;
}
void InitCallstack()
{
#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE
s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline();
#endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE
if( s_shouldResolveSymbolsOffline )
{
TracyDebug("TRACY: enabling offline symbol resolving!\n");
}
DbgHelpInit();
#ifdef TRACY_DBGHELP_LOCK
DBGHELP_LOCK;
#endif
// use TRACY_NO_DBGHELP_INIT_LOAD=1 to disable preloading of driver
// and process module symbol loading at startup time - they will be loaded on demand later
// Sometimes this process can take a very long time and prevent resolving callstack frames
// symbols during that time.
const char* noInitLoadEnv = GetEnvVar( "TRACY_NO_DBGHELP_INIT_LOAD" );
const bool initTimeModuleLoad = !( noInitLoadEnv && noInitLoadEnv[0] == '1' );
if ( !initTimeModuleLoad )
{
TracyDebug("TRACY: skipping init time dbghelper module load\n");
}
DWORD needed;
LPVOID dev[4096];
if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 )
if( initTimeModuleLoad && EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 )
{
char windir[MAX_PATH];
if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 );
@@ -193,7 +437,7 @@ void InitCallstack()
path = full;
}
SymLoadModuleEx( GetCurrentProcess(), nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0 );
DbgHelpLoadSymbolsForModule( path, (DWORD64)dev[i], 0 );
const auto psz = strlen( path );
auto pptr = (char*)tracy_malloc_fast( psz+1 );
@@ -214,7 +458,7 @@ void InitCallstack()
HANDLE proc = GetCurrentProcess();
HMODULE mod[1024];
if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
if( initTimeModuleLoad && EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 )
{
const auto sz = needed / sizeof( HMODULE );
for( size_t i=0; i<sz; i++ )
@@ -222,27 +466,13 @@ void InitCallstack()
MODULEINFO info;
if( GetModuleInformation( proc, mod[i], &info, sizeof( info ) ) != 0 )
{
const auto base = uint64_t( info.lpBaseOfDll );
char name[1024];
const auto res = GetModuleFileNameA( mod[i], name, 1021 );
if( res > 0 )
const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 );
if( nameLength > 0 )
{
// This may be a new module loaded since our call to SymInitialize.
// Just in case, force DbgHelp to load its pdb !
SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0);
auto ptr = name + res;
while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--;
if( ptr > name ) ptr++;
const auto namelen = name + res - ptr;
auto cache = s_modCache->push_next();
cache->start = base;
cache->end = base + info.SizeOfImage;
cache->name = (char*)tracy_malloc_fast( namelen+3 );
cache->name[0] = '[';
memcpy( cache->name+1, ptr, namelen );
cache->name[namelen+1] = ']';
cache->name[namelen+2] = '\0';
LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage );
}
}
}
@@ -259,6 +489,8 @@ void EndCallstack()
const char* DecodeCallstackPtrFast( uint64_t ptr )
{
if( s_shouldResolveSymbolsOffline ) return "[unresolved]";
static char ret[MaxNameSize];
const auto proc = GetCurrentProcess();
@@ -294,7 +526,13 @@ const char* GetKernelModulePath( uint64_t addr )
return it->path;
}
static const char* GetModuleNameAndPrepareSymbols( uint64_t addr )
struct ModuleNameAndBaseAddress
{
const char* name;
uint64_t baseAddr;
};
ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr )
{
if( ( addr >> 63 ) != 0 )
{
@@ -303,17 +541,17 @@ static const char* GetModuleNameAndPrepareSymbols( uint64_t addr )
auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
if( it != s_krnlCache + s_krnlCacheCnt )
{
return it->mod;
return ModuleNameAndBaseAddress{ it->mod, it->addr };
}
}
return "<kernel>";
return ModuleNameAndBaseAddress{ "<kernel>", addr };
}
for( auto& v : *s_modCache )
{
if( addr >= v.start && addr < v.end )
{
return v.name;
return ModuleNameAndBaseAddress{ v.name, v.start };
}
}
@@ -334,35 +572,33 @@ static const char* GetModuleNameAndPrepareSymbols( uint64_t addr )
if( addr >= base && addr < base + info.SizeOfImage )
{
char name[1024];
const auto res = GetModuleFileNameA( mod[i], name, 1021 );
if( res > 0 )
const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 );
if( nameLength > 0 )
{
// since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize)
SymLoadModuleEx(proc, NULL, name, NULL, (DWORD64)info.lpBaseOfDll, info.SizeOfImage, NULL, 0);
auto ptr = name + res;
while( ptr > name && *ptr != '\\' && *ptr != '/' ) ptr--;
if( ptr > name ) ptr++;
const auto namelen = name + res - ptr;
auto cache = s_modCache->push_next();
cache->start = base;
cache->end = base + info.SizeOfImage;
cache->name = (char*)tracy_malloc_fast( namelen+3 );
cache->name[0] = '[';
memcpy( cache->name+1, ptr, namelen );
cache->name[namelen+1] = ']';
cache->name[namelen+2] = '\0';
return cache->name;
ModuleCache* cachedModule = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage );
return ModuleNameAndBaseAddress{ cachedModule->name, cachedModule->start };
}
}
}
}
}
return "[unknown]";
return ModuleNameAndBaseAddress{ "[unknown]", 0x0 };
}
CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
{
CallstackSymbolData sym;
if( s_shouldResolveSymbolsOffline )
{
sym.file = "[unknown]";
sym.line = 0;
sym.needFree = false;
return sym;
}
IMAGEHLP_LINE64 line;
DWORD displacement = 0;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
@@ -390,15 +626,32 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
{
int write;
const auto proc = GetCurrentProcess();
InitRpmalloc();
#ifdef TRACY_DBGHELP_LOCK
DBGHELP_LOCK;
#endif
const auto moduleName = GetModuleNameAndPrepareSymbols(ptr);
InitRpmalloc();
const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr );
if( s_shouldResolveSymbolsOffline )
{
#ifdef TRACY_DBGHELP_LOCK
DBGHELP_UNLOCK;
#endif
cb_data[0].symAddr = ptr - moduleNameAndAddress.baseAddr;
cb_data[0].symLen = 0;
cb_data[0].name = CopyStringFast("[unresolved]");
cb_data[0].file = CopyStringFast("[unknown]");
cb_data[0].line = 0;
return { cb_data, 1, moduleNameAndAddress.name };
}
int write;
const auto proc = GetCurrentProcess();
#if !defined TRACY_NO_CALLSTACK_INLINES
BOOL doInline = FALSE;
@@ -448,7 +701,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
cb_data[write].line = line.LineNumber;
}
cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name );
cb_data[write].file = CopyStringFast( filename );
if( symValid )
{
@@ -481,7 +734,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
cb.line = line.LineNumber;
}
cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleName );
cb.name = symInlineValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name );
cb.file = CopyStringFast( filename );
if( symInlineValid )
{
@@ -502,17 +755,21 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
DBGHELP_UNLOCK;
#endif
return { cb_data, uint8_t( cb_num ), moduleName };
return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name };
}
#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
enum { MaxCbTrace = 64 };
struct backtrace_state* cb_bts;
struct backtrace_state* cb_bts = nullptr;
int cb_num;
CallstackEntry cb_data[MaxCbTrace];
int cb_fixup;
#ifdef TRACY_USE_IMAGE_CACHE
static ImageCache* s_imageCache = nullptr;
#endif //#ifdef TRACY_USE_IMAGE_CACHE
#ifdef TRACY_DEBUGINFOD
debuginfod_client* s_debuginfod;
@@ -525,13 +782,14 @@ struct DebugInfo
int fd;
};
FastVector<DebugInfo> s_di_known( 16 );
static FastVector<DebugInfo>* s_di_known;
#endif
#ifdef __linux
struct KernelSymbol
{
uint64_t addr;
uint32_t size;
const char* name;
const char* mod;
};
@@ -543,10 +801,11 @@ static void InitKernelSymbols()
{
FILE* f = fopen( "/proc/kallsyms", "rb" );
if( !f ) return;
tracy::FastVector<KernelSymbol> tmpSym( 1024 );
tracy::FastVector<KernelSymbol> tmpSym( 512 * 1024 );
size_t linelen = 16 * 1024; // linelen must be big enough to prevent reallocs in getline()
auto linebuf = (char*)tracy_malloc( linelen );
ssize_t sz;
size_t validCnt = 0;
while( ( sz = getline( &linebuf, &linelen, f ) ) != -1 )
{
auto ptr = linebuf;
@@ -579,7 +838,7 @@ static void InitKernelSymbols()
}
if( addr == 0 ) continue;
ptr++;
if( *ptr != 'T' && *ptr != 't' ) continue;
const bool valid = *ptr == 'T' || *ptr == 't';
ptr += 2;
const auto namestart = ptr;
while( *ptr != '\t' && *ptr != '\n' ) ptr++;
@@ -594,20 +853,28 @@ static void InitKernelSymbols()
modend = ptr;
}
auto strname = (char*)tracy_malloc_fast( nameend - namestart + 1 );
memcpy( strname, namestart, nameend - namestart );
strname[nameend-namestart] = '\0';
char* strname = nullptr;
char* strmod = nullptr;
if( modstart )
if( valid )
{
strmod = (char*)tracy_malloc_fast( modend - modstart + 1 );
memcpy( strmod, modstart, modend - modstart );
strmod[modend-modstart] = '\0';
validCnt++;
strname = (char*)tracy_malloc_fast( nameend - namestart + 1 );
memcpy( strname, namestart, nameend - namestart );
strname[nameend-namestart] = '\0';
if( modstart )
{
strmod = (char*)tracy_malloc_fast( modend - modstart + 1 );
memcpy( strmod, modstart, modend - modstart );
strmod[modend-modstart] = '\0';
}
}
auto sym = tmpSym.push_next();
sym->addr = addr;
sym->size = 0;
sym->name = strname;
sym->mod = strmod;
}
@@ -615,11 +882,22 @@ static void InitKernelSymbols()
fclose( f );
if( tmpSym.empty() ) return;
std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr > rhs.addr; } );
s_kernelSymCnt = tmpSym.size();
s_kernelSym = (KernelSymbol*)tracy_malloc_fast( sizeof( KernelSymbol ) * s_kernelSymCnt );
memcpy( s_kernelSym, tmpSym.data(), sizeof( KernelSymbol ) * s_kernelSymCnt );
TracyDebug( "Loaded %zu kernel symbols\n", s_kernelSymCnt );
std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr < rhs.addr; } );
for( size_t i=0; i<tmpSym.size()-1; i++ )
{
if( tmpSym[i].name ) tmpSym[i].size = tmpSym[i+1].addr - tmpSym[i].addr;
}
s_kernelSymCnt = validCnt;
s_kernelSym = (KernelSymbol*)tracy_malloc_fast( sizeof( KernelSymbol ) * validCnt );
auto dst = s_kernelSym;
for( auto& v : tmpSym )
{
if( v.name ) *dst++ = v;
}
assert( dst == s_kernelSym + validCnt );
TracyDebug( "Loaded %zu kernel symbols (%zu code sections)\n", tmpSym.size(), validCnt );
}
#endif
@@ -628,8 +906,7 @@ char* NormalizePath( const char* path )
if( path[0] != '/' ) return nullptr;
const char* ptr = path;
const char* end = path;
while( *end ) end++;
const char* end = path + strlen( path );
char* res = (char*)tracy_malloc( end - ptr + 1 );
size_t rsz = 0;
@@ -685,7 +962,26 @@ void InitCallstackCritical()
void InitCallstack()
{
cb_bts = backtrace_create_state( nullptr, 0, nullptr, nullptr );
InitRpmalloc();
#ifdef TRACY_USE_IMAGE_CACHE
s_imageCache = (ImageCache*)tracy_malloc( sizeof( ImageCache ) );
new(s_imageCache) ImageCache();
#endif //#ifdef TRACY_USE_IMAGE_CACHE
#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE
s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline();
#endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE
if( s_shouldResolveSymbolsOffline )
{
cb_bts = nullptr; // disable use of libbacktrace calls
TracyDebug("TRACY: enabling offline symbol resolving!\n");
}
else
{
cb_bts = backtrace_create_state( nullptr, 0, nullptr, nullptr );
}
#ifndef TRACY_DEMANGLE
___tracy_init_demangle_buffer();
#endif
@@ -695,6 +991,8 @@ void InitCallstack()
#endif
#ifdef TRACY_DEBUGINFOD
s_debuginfod = debuginfod_begin();
s_di_known = (FastVector<DebugInfo>*)tracy_malloc( sizeof( FastVector<DebugInfo> ) );
new (s_di_known) FastVector<DebugInfo>( 16 );
#endif
}
@@ -725,11 +1023,11 @@ DebugInfo* FindDebugInfo( FastVector<DebugInfo>& vec, const uint8_t* buildid_dat
int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size, const char* filename )
{
auto buildid = (uint8_t*)buildid_data;
auto it = FindDebugInfo( s_di_known, buildid, buildid_size );
auto it = FindDebugInfo( *s_di_known, buildid, buildid_size );
if( it ) return it->fd >= 0 ? dup( it->fd ) : -1;
int fd = debuginfod_find_debuginfo( s_debuginfod, buildid, buildid_size, nullptr );
it = s_di_known.push_next();
it = s_di_known->push_next();
it->buildid_size = buildid_size;
it->buildid = (uint8_t*)tracy_malloc( buildid_size );
memcpy( it->buildid, buildid, buildid_size );
@@ -744,7 +1042,7 @@ int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size, const
const uint8_t* GetBuildIdForImage( const char* image, size_t& size )
{
assert( image );
for( auto& v : s_di_known )
for( auto& v : *s_di_known )
{
if( strcmp( image, v.filename ) == 0 )
{
@@ -763,11 +1061,21 @@ debuginfod_client* GetDebuginfodClient()
void EndCallstack()
{
#ifdef TRACY_USE_IMAGE_CACHE
if( s_imageCache )
{
s_imageCache->~ImageCache();
tracy_free( s_imageCache );
}
#endif //#ifdef TRACY_USE_IMAGE_CACHE
#ifndef TRACY_DEMANGLE
___tracy_free_demangle_buffer();
#endif
#ifdef TRACY_DEBUGINFOD
ClearDebugInfoVector( s_di_known );
ClearDebugInfoVector( *s_di_known );
s_di_known->~FastVector<DebugInfo>();
tracy_free( s_di_known );
debuginfod_end( s_debuginfod );
#endif
}
@@ -824,7 +1132,15 @@ static void SymbolAddressErrorCb( void* data, const char* /*msg*/, int /*errnum*
CallstackSymbolData DecodeSymbolAddress( uint64_t ptr )
{
CallstackSymbolData sym;
backtrace_pcinfo( cb_bts, ptr, SymbolAddressDataCb, SymbolAddressErrorCb, &sym );
if( cb_bts )
{
backtrace_pcinfo( cb_bts, ptr, SymbolAddressDataCb, SymbolAddressErrorCb, &sym );
}
else
{
SymbolAddressErrorCb(&sym, nullptr, 0);
}
return sym;
}
@@ -927,33 +1243,67 @@ void SymInfoError( void* /*data*/, const char* /*msg*/, int /*errnum*/ )
cb_data[cb_num-1].symAddr = 0;
}
void GetSymbolForOfflineResolve(void* address, uint64_t imageBaseAddress, CallstackEntry& cbEntry)
{
// tagged with a string that we can identify as an unresolved symbol
cbEntry.name = CopyStringFast( "[unresolved]" );
// set .so relative offset so it can be resolved offline
cbEntry.symAddr = (uint64_t)address - imageBaseAddress;
cbEntry.symLen = 0x0;
cbEntry.file = CopyStringFast( "[unknown]" );
cbEntry.line = 0;
}
CallstackEntryData DecodeCallstackPtr( uint64_t ptr )
{
InitRpmalloc();
if( ptr >> 63 == 0 )
{
cb_num = 0;
backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
assert( cb_num > 0 );
const char* imageName = nullptr;
uint64_t imageBaseAddress = 0x0;
backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr );
const char* symloc = nullptr;
#ifdef TRACY_USE_IMAGE_CACHE
const auto* image = s_imageCache->GetImageForAddress((void*)ptr);
if( image )
{
imageName = image->m_name;
imageBaseAddress = uint64_t(image->m_startAddress);
}
#else
Dl_info dlinfo;
if( dladdr( (void*)ptr, &dlinfo ) ) symloc = dlinfo.dli_fname;
if( dladdr( (void*)ptr, &dlinfo ) )
{
imageName = dlinfo.dli_fname;
imageBaseAddress = uint64_t( dlinfo.dli_fbase );
}
#endif
return { cb_data, uint8_t( cb_num ), symloc ? symloc : "[unknown]" };
if( s_shouldResolveSymbolsOffline )
{
cb_num = 1;
GetSymbolForOfflineResolve( (void*)ptr, imageBaseAddress, cb_data[0] );
}
else
{
cb_num = 0;
backtrace_pcinfo( cb_bts, ptr, CallstackDataCb, CallstackErrorCb, nullptr );
assert( cb_num > 0 );
backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr );
}
return { cb_data, uint8_t( cb_num ), imageName ? imageName : "[unknown]" };
}
#ifdef __linux
else if( s_kernelSym )
{
auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } );
auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr + lhs.size < rhs; } );
if( it != s_kernelSym + s_kernelSymCnt )
{
cb_data[0].name = CopyStringFast( it->name );
cb_data[0].file = CopyStringFast( "<kernel>" );
cb_data[0].line = 0;
cb_data[0].symLen = 0;
cb_data[0].symLen = it->size;
cb_data[0].symAddr = it->addr;
return { cb_data, 1, it->mod ? it->mod : "<kernel>" };
}

View File

@@ -3,22 +3,28 @@
#include "TracyCallstack.h"
#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5
# include <unwind.h>
#elif TRACY_HAS_CALLSTACK >= 3
# include <execinfo.h>
#endif
#ifndef TRACY_HAS_CALLSTACK
namespace tracy
{
static tracy_force_inline void* Callstack( int depth ) { return nullptr; }
static constexpr bool has_callstack() { return false; }
static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; }
}
#else
#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 5
# include <unwind.h>
#elif TRACY_HAS_CALLSTACK >= 3
# ifdef TRACY_LIBUNWIND_BACKTRACE
// libunwind is, in general, significantly faster than execinfo based backtraces
# define UNW_LOCAL_ONLY
# include <libunwind.h>
# else
# include <execinfo.h>
# endif
#endif
#ifdef TRACY_DEBUGINFOD
# include <elfutils/debuginfod.h>
#endif
@@ -31,6 +37,8 @@ static tracy_force_inline void* Callstack( int depth ) { return nullptr; }
namespace tracy
{
static constexpr bool has_callstack() { return true; }
struct CallstackSymbolData
{
const char* file;
@@ -72,11 +80,10 @@ debuginfod_client* GetDebuginfodClient();
extern "C"
{
typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long );
TRACY_API extern ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain;
TRACY_API unsigned long ___tracy_RtlWalkFrameChain( void**, unsigned long, unsigned long );
}
static tracy_force_inline void* Callstack( int depth )
static tracy_force_inline void* Callstack( int32_t depth )
{
assert( depth >= 1 && depth < 63 );
auto trace = (uintptr_t*)tracy_malloc( ( 1 + depth ) * sizeof( uintptr_t ) );
@@ -105,7 +112,7 @@ static _Unwind_Reason_Code tracy_unwind_callback( struct _Unwind_Context* ctx, v
return _URC_NO_REASON;
}
static tracy_force_inline void* Callstack( int depth )
static tracy_force_inline void* Callstack( int32_t depth )
{
assert( depth >= 1 && depth < 63 );
@@ -120,12 +127,18 @@ static tracy_force_inline void* Callstack( int depth )
#elif TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6
static tracy_force_inline void* Callstack( int depth )
static tracy_force_inline void* Callstack( int32_t depth )
{
assert( depth >= 1 );
auto trace = (uintptr_t*)tracy_malloc( ( 1 + (size_t)depth ) * sizeof( uintptr_t ) );
#ifdef TRACY_LIBUNWIND_BACKTRACE
size_t num = unw_backtrace( (void**)(trace+1), depth );
#else
const auto num = (size_t)backtrace( (void**)(trace+1), depth );
#endif
*trace = num;
return trace;

View File

@@ -0,0 +1,121 @@
#ifdef __linux__
#include <algorithm>
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include "TracyDebug.hpp"
#include "TracyKCore.hpp"
#include "../common/TracyAlloc.hpp"
#if !defined(__GLIBC__) && !defined(__WORDSIZE)
// include __WORDSIZE headers for musl
# include <bits/reg.h>
#endif
namespace tracy
{
using elf_half = uint16_t;
using elf_word = uint32_t;
using elf_sword = int32_t;
#if __WORDSIZE == 32
using elf_addr = uint32_t;
using elf_off = uint32_t;
using elf_xword = uint32_t;
#else
using elf_addr = uint64_t;
using elf_off = uint64_t;
using elf_xword = uint64_t;
#endif
struct elf_ehdr
{
unsigned char e_ident[16];
elf_half e_type;
elf_half e_machine;
elf_word e_version;
elf_addr e_entry;
elf_off e_phoff;
elf_off e_shoff;
elf_word e_flags;
elf_half e_ehsize;
elf_half e_phentsize;
elf_half e_phnum;
elf_half e_shentsize;
elf_half e_shnum;
elf_half e_shstrndx;
};
struct elf_phdr
{
elf_word p_type;
elf_word p_flags;
elf_off p_offset;
elf_addr p_vaddr;
elf_addr p_paddr;
elf_xword p_filesz;
elf_xword p_memsz;
uint64_t p_align; // include 32-bit-only flags field for 32-bit compatibility
};
KCore::KCore()
: m_offsets( 16 )
{
m_fd = open( "/proc/kcore", O_RDONLY );
if( m_fd == -1 ) return;
elf_ehdr ehdr;
if( read( m_fd, &ehdr, sizeof( ehdr ) ) != sizeof( ehdr ) ) goto err;
assert( ehdr.e_phentsize == sizeof( elf_phdr ) );
for( elf_half i=0; i<ehdr.e_phnum; i++ )
{
elf_phdr phdr;
if( lseek( m_fd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET ) == -1 ) goto err;
if( read( m_fd, &phdr, sizeof( phdr ) ) != sizeof( phdr ) ) goto err;
if( phdr.p_type != 1 ) continue;
auto ptr = m_offsets.push_next();
ptr->start = phdr.p_vaddr;
ptr->size = phdr.p_memsz;
ptr->offset = phdr.p_offset;
}
std::sort( m_offsets.begin(), m_offsets.end(), []( const Offset& lhs, const Offset& rhs ) { return lhs.start < rhs.start; } );
TracyDebug( "KCore: %zu segments found\n", m_offsets.size() );
return;
err:
close( m_fd );
m_fd = -1;
}
KCore::~KCore()
{
if( m_fd != -1 ) close( m_fd );
}
void* KCore::Retrieve( uint64_t addr, uint64_t size ) const
{
if( m_fd == -1 ) return nullptr;
auto it = std::lower_bound( m_offsets.begin(), m_offsets.end(), addr, []( const Offset& lhs, uint64_t rhs ) { return lhs.start + lhs.size < rhs; } );
if( it == m_offsets.end() ) return nullptr;
if( addr + size > it->start + it->size ) return nullptr;
if( lseek( m_fd, it->offset + addr - it->start, SEEK_SET ) == -1 ) return nullptr;
auto ptr = tracy_malloc( size );
if( read( m_fd, ptr, size ) != ssize_t( size ) )
{
tracy_free( ptr );
return nullptr;
}
return ptr;
}
}
#endif

View File

@@ -0,0 +1,37 @@
#ifndef __TRACYKCORE_HPP__
#define __TRACYKCORE_HPP__
#ifdef __linux__
#include <stdint.h>
#include "TracyFastVector.hpp"
namespace tracy
{
class KCore
{
struct Offset
{
uint64_t start;
uint64_t size;
uint64_t offset;
};
public:
KCore();
~KCore();
void* Retrieve( uint64_t addr, uint64_t size ) const;
private:
int m_fd;
FastVector<Offset> m_offsets;
};
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
#include "tracy_concurrentqueue.h"
#include "tracy_SPSCQueue.h"
#include "TracyCallstack.hpp"
#include "TracyKCore.hpp"
#include "TracySysPower.hpp"
#include "TracySysTime.hpp"
#include "TracyFastVector.hpp"
@@ -27,7 +28,7 @@
# include <mach/mach_time.h>
#endif
#if ( defined _WIN32 || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) )
#if ( (defined _WIN32 && !(defined _M_ARM64 || defined _M_ARM)) || ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 ) || ( defined TARGET_OS_IOS && TARGET_OS_IOS == 1 ) )
# define TRACY_HW_TIMER
#endif
@@ -44,6 +45,10 @@ namespace tracy
#if defined(TRACY_DELAYED_INIT) && defined(TRACY_MANUAL_LIFETIME)
TRACY_API void StartupProfiler();
TRACY_API void ShutdownProfiler();
TRACY_API bool IsProfilerStarted();
# define TracyIsStarted tracy::IsProfilerStarted()
#else
# define TracyIsStarted true
#endif
class GpuCtx;
@@ -92,11 +97,11 @@ struct LuaZoneState
#define TracyLfqPrepare( _type ) \
moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \
auto __token = GetToken(); \
tracy::moodycamel::ConcurrentQueueDefaultTraits::index_t __magic; \
auto __token = tracy::GetToken(); \
auto& __tail = __token->get_tail_index(); \
auto item = __token->enqueue_begin( __magic ); \
MemWrite( &item->hdr.type, _type );
tracy::MemWrite( &item->hdr.type, _type );
#define TracyLfqCommit \
__tail.store( __magic + 1, std::memory_order_release );
@@ -114,11 +119,11 @@ struct LuaZoneState
#ifdef TRACY_FIBERS
# define TracyQueuePrepare( _type ) \
auto item = Profiler::QueueSerial(); \
MemWrite( &item->hdr.type, _type );
auto item = tracy::Profiler::QueueSerial(); \
tracy::MemWrite( &item->hdr.type, _type );
# define TracyQueueCommit( _name ) \
MemWrite( &item->_name.thread, GetThreadHandle() ); \
Profiler::QueueSerialFinish();
tracy::MemWrite( &item->_name.thread, tracy::GetThreadHandle() ); \
tracy::Profiler::QueueSerialFinish();
# define TracyQueuePrepareC( _type ) \
auto item = tracy::Profiler::QueueSerial(); \
tracy::MemWrite( &item->hdr.type, _type );
@@ -278,6 +283,8 @@ public:
static void MemFreeNamed( const void* ptr, bool secure, const char* name );
static void MemAllocCallstackNamed( const void* ptr, size_t size, int depth, bool secure, const char* name );
static void MemFreeCallstackNamed( const void* ptr, int depth, bool secure, const char* name );
static void MemDiscard( const char* name, bool secure );
static void MemDiscardCallstack( const char* name, bool secure, int32_t depth );
static void SendCallstack( int depth );
static void ParameterRegister( ParameterCallback cb, void* data );
static void ParameterSetup( uint32_t idx, const char* name, bool isBool, int32_t val );
@@ -290,11 +297,12 @@ public:
}
#ifdef TRACY_FIBERS
static tracy_force_inline void EnterFiber( const char* fiber )
static tracy_force_inline void EnterFiber( const char* fiber, int32_t groupHint )
{
TracyQueuePrepare( QueueType::FiberEnter );
MemWrite( &item->fiberEnter.time, GetTime() );
MemWrite( &item->fiberEnter.fiber, (uint64_t)fiber );
MemWrite( &item->fiberEnter.groupHint, groupHint );
TracyQueueCommit( fiberEnter );
}
@@ -306,7 +314,7 @@ public:
}
#endif
void SendCallstack( int depth, const char* skipBefore );
void SendCallstack( int32_t depth, const char* skipBefore );
static void CutCallstack( void* callstack, const char* skipBefore );
static bool ShouldExit();
@@ -359,29 +367,29 @@ public:
// 1b null terminator
// nsz zone name (optional)
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function )
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, uint32_t color = 0 )
{
return AllocSourceLocation( line, source, function, nullptr, 0 );
return AllocSourceLocation( line, source, function, nullptr, 0, color );
}
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, const char* name, size_t nameSz )
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, const char* function, const char* name, size_t nameSz, uint32_t color = 0 )
{
return AllocSourceLocation( line, source, strlen(source), function, strlen(function), name, nameSz );
return AllocSourceLocation( line, source, strlen(source), function, strlen(function), name, nameSz, color );
}
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz )
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, uint32_t color = 0 )
{
return AllocSourceLocation( line, source, sourceSz, function, functionSz, nullptr, 0 );
return AllocSourceLocation( line, source, sourceSz, function, functionSz, nullptr, 0, color );
}
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz )
static tracy_force_inline uint64_t AllocSourceLocation( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color = 0 )
{
const auto sz32 = uint32_t( 2 + 4 + 4 + functionSz + 1 + sourceSz + 1 + nameSz );
assert( sz32 <= (std::numeric_limits<uint16_t>::max)() );
const auto sz = uint16_t( sz32 );
auto ptr = (char*)tracy_malloc( sz );
memcpy( ptr, &sz, 2 );
memset( ptr + 2, 0, 4 );
memcpy( ptr + 2, &color, 4 );
memcpy( ptr + 6, &line, 4 );
memcpy( ptr + 10, function, functionSz );
ptr[10 + functionSz] = '\0';
@@ -412,6 +420,9 @@ private:
void HandleSymbolQueueItem( const SymbolQueueItem& si );
#endif
void InstallCrashHandler();
void RemoveCrashHandler();
void ClearQueues( tracy::moodycamel::ConsumerToken& token );
void ClearSerial();
DequeueStatus Dequeue( tracy::moodycamel::ConsumerToken& token );
@@ -444,6 +455,21 @@ private:
m_bufferOffset += int( len );
}
char* SafeCopyProlog( const char* p, size_t size );
void SafeCopyEpilog( char* buf );
template<class Callable> // must be void( const char* buf, size_t size )
bool WithSafeCopy( const char* p, size_t size, Callable&& callable )
{
if( char* buf = SafeCopyProlog( p, size ) )
{
callable( buf, size );
SafeCopyEpilog( buf );
return true;
}
return false;
}
bool SendData( const char* data, size_t len );
void SendLongString( uint64_t ptr, const char* str, size_t len, QueueType type );
void SendSourceLocation( uint64_t ptr );
@@ -473,14 +499,13 @@ private:
static tracy_force_inline void SendCallstackSerial( void* ptr )
{
#ifdef TRACY_HAS_CALLSTACK
auto item = GetProfiler().m_serialQueue.prepare_next();
MemWrite( &item->hdr.type, QueueType::CallstackSerial );
MemWrite( &item->callstackFat.ptr, (uint64_t)ptr );
GetProfiler().m_serialQueue.commit_next();
#else
static_cast<void>(ptr); // unused
#endif
if( has_callstack() )
{
auto item = GetProfiler().m_serialQueue.prepare_next();
MemWrite( &item->hdr.type, QueueType::CallstackSerial );
MemWrite( &item->callstackFat.ptr, (uint64_t)ptr );
GetProfiler().m_serialQueue.commit_next();
}
}
static tracy_force_inline void SendMemAlloc( QueueType type, const uint32_t thread, const void* ptr, size_t size )
@@ -518,6 +543,18 @@ private:
GetProfiler().m_serialQueue.commit_next();
}
static tracy_force_inline void SendMemDiscard( QueueType type, const uint32_t thread, const char* name )
{
assert( type == QueueType::MemDiscard || type == QueueType::MemDiscardCallstack );
auto item = GetProfiler().m_serialQueue.prepare_next();
MemWrite( &item->hdr.type, type );
MemWrite( &item->memDiscard.time, GetTime() );
MemWrite( &item->memDiscard.thread, thread );
MemWrite( &item->memDiscard.name, (uint64_t)name );
GetProfiler().m_serialQueue.commit_next();
}
static tracy_force_inline void SendMemName( const char* name )
{
assert( name );
@@ -601,13 +638,24 @@ private:
char* m_queryData;
char* m_queryDataPtr;
#if defined _WIN32
void* m_exceptionHandler;
#ifndef NDEBUG
// m_safeSendBuffer and m_pipe should only be used by the Tracy Profiler thread; this ensures that in debug builds.
std::atomic_bool m_inUse{ false };
#endif
char* m_safeSendBuffer;
#if defined _WIN32
void* m_prevHandler;
#else
int m_pipe[2];
int m_pipeBufSize;
#endif
#ifdef __linux__
struct {
struct sigaction pwr, ill, fpe, segv, pipe, bus, abrt;
} m_prevSignal;
KCore* m_kcore;
#endif
bool m_crashHandlerInstalled;

View File

@@ -2,6 +2,7 @@
#define __TRACYSCOPED_HPP__
#include <limits>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
@@ -9,6 +10,8 @@
#include "../common/TracyAlign.hpp"
#include "../common/TracyAlloc.hpp"
#include "../client/TracyLock.hpp"
#include "TracyProfiler.hpp"
#include "TracyCallstack.hpp"
namespace tracy
{
@@ -34,7 +37,7 @@ void ScopedZone::End()
TracyQueueCommit( zoneEndThread );
}
ScopedZone::ScopedZone( const SourceLocationData* srcloc, bool is_active )
ScopedZone::ScopedZone( const SourceLocationData* srcloc, int32_t depth, bool is_active )
#ifdef TRACY_ON_DEMAND
: m_active( is_active && GetProfiler().IsConnected() )
#else
@@ -45,13 +48,19 @@ ScopedZone::ScopedZone( const SourceLocationData* srcloc, bool is_active )
#ifdef TRACY_ON_DEMAND
m_connectionId = GetProfiler().ConnectionId();
#endif
TracyQueuePrepare( QueueType::ZoneBegin );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc );
TracyQueueCommit( zoneBeginThread );
}
auto zoneQueue = QueueType::ZoneBegin;
if( depth > 0 && has_callstack() )
{
GetProfiler().SendCallstack( depth );
zoneQueue = QueueType::ZoneBeginCallstack;
}
TracyQueuePrepare( zoneQueue );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc );
TracyQueueCommit( zoneBeginThread );
}
ScopedZone::ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active )
ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int32_t depth, bool is_active )
#ifdef TRACY_ON_DEMAND
: m_active( is_active && GetProfiler().IsConnected() )
#else
@@ -62,51 +71,21 @@ ScopedZone::ScopedZone( const SourceLocationData* srcloc, int depth, bool is_act
#ifdef TRACY_ON_DEMAND
m_connectionId = GetProfiler().ConnectionId();
#endif
GetProfiler().SendCallstack( depth );
auto zoneQueue = QueueType::ZoneBeginAllocSrcLoc;
if( depth > 0 && has_callstack() )
{
GetProfiler().SendCallstack( depth );
zoneQueue = QueueType::ZoneBeginAllocSrcLocCallstack;
}
TracyQueuePrepare( zoneQueue );
const auto srcloc =
Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz, color );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, srcloc );
TracyQueueCommit( zoneBeginThread );
}
TracyQueuePrepare( QueueType::ZoneBeginCallstack );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, (uint64_t)srcloc );
TracyQueueCommit( zoneBeginThread );
}
ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active )
#ifdef TRACY_ON_DEMAND
: m_active( is_active && GetProfiler().IsConnected() )
#else
: m_active( is_active )
#endif
{
if( !m_active ) return;
#ifdef TRACY_ON_DEMAND
m_connectionId = GetProfiler().ConnectionId();
#endif
TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLoc );
const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, srcloc );
TracyQueueCommit( zoneBeginThread );
}
ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active )
#ifdef TRACY_ON_DEMAND
: m_active( is_active && GetProfiler().IsConnected() )
#else
: m_active( is_active )
#endif
{
if( !m_active ) return;
#ifdef TRACY_ON_DEMAND
m_connectionId = GetProfiler().ConnectionId();
#endif
GetProfiler().SendCallstack( depth );
TracyQueuePrepare( QueueType::ZoneBeginAllocSrcLocCallstack );
const auto srcloc = Profiler::AllocSourceLocation( line, source, sourceSz, function, functionSz, name, nameSz );
MemWrite( &item->zoneBegin.time, Profiler::GetTime() );
MemWrite( &item->zoneBegin.srcloc, srcloc );
TracyQueueCommit( zoneBeginThread );
}
ScopedZone::ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int32_t depth, bool is_active ) : ScopedZone( line, source, sourceSz, function, functionSz, name, nameSz, 0, depth, is_active ) {}
ScopedZone::~ScopedZone()
{
@@ -150,6 +129,30 @@ void ScopedZone::Text( const Char* txt, size_t size )
TracyQueueCommit( zoneTextFatThread );
}
void ScopedZone::TextFmt( const char* fmt, ... )
{
if( !m_active ) return;
#ifdef TRACY_ON_DEMAND
if( GetProfiler().ConnectionId() != m_connectionId ) return;
#endif
va_list args;
va_start( args, fmt );
auto size = vsnprintf( nullptr, 0, fmt, args );
va_end( args );
if( size < 0 ) return;
assert( size < (std::numeric_limits<uint16_t>::max)() );
char* ptr = (char*)tracy_malloc( size_t( size ) + 1 );
va_start( args, fmt );
vsnprintf( ptr, size_t( size ) + 1, fmt, args );
va_end( args );
TracyQueuePrepare( QueueType::ZoneText );
MemWrite( &item->zoneTextFat.text, (uint64_t)ptr );
MemWrite( &item->zoneTextFat.size, (uint16_t)size );
TracyQueueCommit( zoneTextFatThread );
}
void ScopedZone::Name( const char* txt, size_t size )
{
assert( size < (std::numeric_limits<uint16_t>::max)() );
@@ -181,6 +184,30 @@ void ScopedZone::Name( const Char* txt, size_t size )
TracyQueueCommit( zoneTextFatThread );
}
void ScopedZone::NameFmt( const char* fmt, ... )
{
if( !m_active ) return;
#ifdef TRACY_ON_DEMAND
if( GetProfiler().ConnectionId() != m_connectionId ) return;
#endif
va_list args;
va_start( args, fmt );
auto size = vsnprintf( nullptr, 0, fmt, args );
va_end( args );
if( size < 0 ) return;
assert( size < (std::numeric_limits<uint16_t>::max)() );
char* ptr = (char*)tracy_malloc( size_t( size ) + 1 );
va_start( args, fmt );
vsnprintf( ptr, size_t( size ) + 1, fmt, args );
va_end( args );
TracyQueuePrepare( QueueType::ZoneName );
MemWrite( &item->zoneTextFat.text, (uint64_t)ptr );
MemWrite( &item->zoneTextFat.size, (uint16_t)size );
TracyQueueCommit( zoneTextFatThread );
}
void ScopedZone::Color( uint32_t color )
{
if( !m_active ) return;

View File

@@ -85,7 +85,7 @@ void SysPower::ScanDirectory( const char* path, int parent )
FILE* f = fopen( tmp, "r" );
if( f )
{
fscanf( f, "%" PRIu64, &maxRange );
(void)fscanf( f, "%" PRIu64, &maxRange );
fclose( f );
}
}

View File

@@ -16,16 +16,25 @@
namespace tracy
{
static constexpr int GetSamplingFrequency()
static int GetSamplingFrequency()
{
int samplingHz = TRACY_SAMPLING_HZ;
auto env = GetEnvVar( "TRACY_SAMPLING_HZ" );
if( env )
{
int val = atoi( env );
if( val > 0 ) samplingHz = val;
}
#if defined _WIN32
return TRACY_SAMPLING_HZ > 8000 ? 8000 : ( TRACY_SAMPLING_HZ < 1 ? 1 : TRACY_SAMPLING_HZ );
return samplingHz > 8000 ? 8000 : ( samplingHz < 1 ? 1 : samplingHz );
#else
return TRACY_SAMPLING_HZ > 1000000 ? 1000000 : ( TRACY_SAMPLING_HZ < 1 ? 1 : TRACY_SAMPLING_HZ );
return samplingHz > 1000000 ? 1000000 : ( samplingHz < 1 ? 1 : samplingHz );
#endif
}
static constexpr int GetSamplingPeriod()
static int GetSamplingPeriod()
{
return 1000000000 / GetSamplingFrequency();
}
@@ -164,8 +173,11 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
MemWrite( &item->contextSwitch.oldThread, cswitch->oldThreadId );
MemWrite( &item->contextSwitch.newThread, cswitch->newThreadId );
MemWrite( &item->contextSwitch.cpu, record->BufferContext.ProcessorNumber );
MemWrite( &item->contextSwitch.reason, cswitch->oldThreadWaitReason );
MemWrite( &item->contextSwitch.state, cswitch->oldThreadState );
MemWrite( &item->contextSwitch.oldThreadWaitReason, cswitch->oldThreadWaitReason );
MemWrite( &item->contextSwitch.oldThreadState, cswitch->oldThreadState );
MemWrite( &item->contextSwitch.newThreadPriority, cswitch->newThreadPriority );
MemWrite( &item->contextSwitch.oldThreadPriority, cswitch->oldThreadPriority );
MemWrite( &item->contextSwitch.previousCState, cswitch->previousCState );
TracyLfqCommit;
}
else if( hdr.EventDescriptor.Opcode == 50 )
@@ -174,7 +186,10 @@ void WINAPI EventRecordCallback( PEVENT_RECORD record )
TracyLfqPrepare( QueueType::ThreadWakeup );
MemWrite( &item->threadWakeup.time, hdr.TimeStamp.QuadPart );
MemWrite( &item->threadWakeup.cpu, record->BufferContext.ProcessorNumber );
MemWrite( &item->threadWakeup.thread, rt->threadId );
MemWrite( &item->threadWakeup.adjustReason, rt->adjustReason );
MemWrite( &item->threadWakeup.adjustIncrement, rt->adjustIncrement );
TracyLfqCommit;
}
else if( hdr.EventDescriptor.Opcode == 1 || hdr.EventDescriptor.Opcode == 3 )
@@ -321,7 +336,7 @@ static void SetupVsync()
#endif
}
static constexpr int GetSamplingInterval()
static int GetSamplingInterval()
{
return GetSamplingPeriod() / 100;
}
@@ -489,11 +504,11 @@ void SysTraceGetExternalName( uint64_t thread, const char*& threadName, const ch
if( _GetThreadDescription )
{
PWSTR tmp;
_GetThreadDescription( hnd, &tmp );
char buf[256];
if( tmp )
if ( SUCCEEDED( _GetThreadDescription( hnd, &tmp ) ) )
{
char buf[256];
auto ret = wcstombs( buf, tmp, 256 );
LocalFree(tmp);
if( ret != 0 )
{
threadName = CopyString( buf, ret );
@@ -669,7 +684,7 @@ enum TraceEventId
EventBranchMiss,
EventVsync,
EventContextSwitch,
EventWakeup,
EventWaking,
};
static void ProbePreciseIp( perf_event_attr& pe, unsigned long long config0, unsigned long long config1, pid_t pid )
@@ -758,16 +773,16 @@ bool SysTraceStart( int64_t& samplingPeriod )
TracyDebug( "perf_event_paranoid: %i\n", paranoidLevel );
#endif
int switchId = -1, wakeupId = -1, vsyncId = -1;
int switchId = -1, wakingId = -1, vsyncId = -1;
const auto switchIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_switch/id" );
if( switchIdStr ) switchId = atoi( switchIdStr );
const auto wakeupIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_wakeup/id" );
if( wakeupIdStr ) wakeupId = atoi( wakeupIdStr );
const auto wakingIdStr = ReadFile( "/sys/kernel/debug/tracing/events/sched/sched_waking/id" );
if( wakingIdStr ) wakingId = atoi( wakingIdStr );
const auto vsyncIdStr = ReadFile( "/sys/kernel/debug/tracing/events/drm/drm_vblank_event/id" );
if( vsyncIdStr ) vsyncId = atoi( vsyncIdStr );
TracyDebug( "sched_switch id: %i\n", switchId );
TracyDebug( "sched_wakeup id: %i\n", wakeupId );
TracyDebug( "sched_waking id: %i\n", wakingId );
TracyDebug( "drm_vblank_event id: %i\n", vsyncId );
#ifdef TRACY_NO_SAMPLING
@@ -822,7 +837,7 @@ bool SysTraceStart( int64_t& samplingPeriod )
2 + // CPU cycles + instructions retired
2 + // cache reference + miss
2 + // branch retired + miss
2 + // context switches + wakeups
2 + // context switches + waking ups
1 // vsync
);
s_ring = (RingBuffer*)tracy_malloc( sizeof( RingBuffer ) * maxNumBuffers );
@@ -1067,18 +1082,31 @@ bool SysTraceStart( int64_t& samplingPeriod )
}
}
if( wakeupId != -1 )
if( wakingId != -1 )
{
pe.config = wakeupId;
pe.config &= ~PERF_SAMPLE_CALLCHAIN;
pe = {};
pe.type = PERF_TYPE_TRACEPOINT;
pe.size = sizeof( perf_event_attr );
pe.sample_period = 1;
pe.sample_type = PERF_SAMPLE_TIME | PERF_SAMPLE_RAW;
// Coult ask for callstack here
//pe.sample_type |= PERF_SAMPLE_CALLCHAIN;
pe.disabled = 1;
pe.inherit = 1;
pe.config = wakingId;
pe.read_format = 0;
#if !defined TRACY_HW_TIMER || !( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
pe.use_clockid = 1;
pe.clockid = CLOCK_MONOTONIC_RAW;
#endif
TracyDebug( "Setup wakeup capture\n" );
TracyDebug( "Setup waking up capture\n" );
for( int i=0; i<s_numCpus; i++ )
{
const int fd = perf_event_open( &pe, -1, i, -1, PERF_FLAG_FD_CLOEXEC );
if( fd != -1 )
{
new( s_ring+s_numBuffers ) RingBuffer( 64*1024, fd, EventWakeup, i );
new( s_ring+s_numBuffers ) RingBuffer( 64*1024, fd, EventWaking, i );
if( s_ring[s_numBuffers].IsValid() )
{
s_numBuffers++;
@@ -1323,6 +1351,7 @@ void SysTraceWorker( void* ptr )
hadData = true;
while( activeNum > 0 )
{
// Find the earliest event from the active buffers
int sel = -1;
int selPos;
int64_t t0 = std::numeric_limits<int64_t>::max();
@@ -1360,6 +1389,7 @@ void SysTraceWorker( void* ptr )
}
}
}
// Found any event
if( sel >= 0 )
{
auto& ring = ringArray[ctxBufferIdx + sel];
@@ -1375,10 +1405,10 @@ void SysTraceWorker( void* ptr )
const auto rid = ring.GetId();
if( rid == EventContextSwitch )
{
// Layout:
// u64 time
// u64 cnt
// u64 ip[cnt]
// Layout: See /sys/kernel/debug/tracing/events/sched/sched_switch/format
// u64 time // PERF_SAMPLE_TIME
// u64 cnt // PERF_SAMPLE_CALLCHAIN
// u64 ip[cnt] // PERF_SAMPLE_CALLCHAIN
// u32 size
// u8 data[size]
// Data (not ABI stable, but has not changed since it was added, in 2009):
@@ -1399,35 +1429,43 @@ void SysTraceWorker( void* ptr )
const auto traceOffset = offset;
offset += sizeof( uint64_t ) * cnt + sizeof( uint32_t ) + 8 + 16;
uint32_t prev_pid, next_pid;
uint32_t prev_pid, prev_prio;
uint32_t next_pid, next_prio;
long prev_state;
ring.Read( &prev_pid, offset, sizeof( uint32_t ) );
offset += sizeof( uint32_t ) + sizeof( uint32_t );
offset += sizeof( uint32_t );
ring.Read( &prev_prio, offset, sizeof( uint32_t ) );
offset += sizeof( uint32_t );
ring.Read( &prev_state, offset, sizeof( long ) );
offset += sizeof( long ) + 16;
ring.Read( &next_pid, offset, sizeof( uint32_t ) );
offset += sizeof( uint32_t );
ring.Read( &next_prio, offset, sizeof( uint32_t ) );
uint8_t reason = 100;
uint8_t state;
uint8_t oldThreadWaitReason = 100;
uint8_t oldThreadState;
if( prev_state & 0x0001 ) state = 104;
else if( prev_state & 0x0002 ) state = 101;
else if( prev_state & 0x0004 ) state = 105;
else if( prev_state & 0x0008 ) state = 106;
else if( prev_state & 0x0010 ) state = 108;
else if( prev_state & 0x0020 ) state = 109;
else if( prev_state & 0x0040 ) state = 110;
else if( prev_state & 0x0080 ) state = 102;
else state = 103;
if( prev_state & 0x0001 ) oldThreadState = 104;
else if( prev_state & 0x0002 ) oldThreadState = 101;
else if( prev_state & 0x0004 ) oldThreadState = 105;
else if( prev_state & 0x0008 ) oldThreadState = 106;
else if( prev_state & 0x0010 ) oldThreadState = 108;
else if( prev_state & 0x0020 ) oldThreadState = 109;
else if( prev_state & 0x0040 ) oldThreadState = 110;
else if( prev_state & 0x0080 ) oldThreadState = 102;
else oldThreadState = 103;
TracyLfqPrepare( QueueType::ContextSwitch );
MemWrite( &item->contextSwitch.time, t0 );
MemWrite( &item->contextSwitch.oldThread, prev_pid );
MemWrite( &item->contextSwitch.newThread, next_pid );
MemWrite( &item->contextSwitch.cpu, uint8_t( ring.GetCpu() ) );
MemWrite( &item->contextSwitch.reason, reason );
MemWrite( &item->contextSwitch.state, state );
MemWrite( &item->contextSwitch.oldThreadWaitReason, oldThreadWaitReason );
MemWrite( &item->contextSwitch.oldThreadState, oldThreadState );
MemWrite( &item->contextSwitch.previousCState, uint8_t( 0 ) );
MemWrite( &item->contextSwitch.newThreadPriority, int8_t( next_prio ) );
MemWrite( &item->contextSwitch.oldThreadPriority, int8_t( prev_prio ) );
TracyLfqCommit;
if( cnt > 0 && prev_pid != 0 && CurrentProcOwnsThread( prev_pid ) )
@@ -1441,27 +1479,33 @@ void SysTraceWorker( void* ptr )
TracyLfqCommit;
}
}
else if( rid == EventWakeup )
else if( rid == EventWaking)
{
// See /sys/kernel/debug/tracing/events/sched/sched_waking/format
// Layout:
// u64 time
// u64 time // PERF_SAMPLE_TIME
// u32 size
// u8 data[size]
// Data:
// u8 hdr[8]
// u8 comm[16]
// u32 pid
// u32 prio
// u64 target_cpu
offset += sizeof( perf_event_header ) + sizeof( uint64_t ) + sizeof( uint32_t ) + 8 + 16;
// i32 prio
// i32 target_cpu
const uint32_t dataOffset = sizeof( perf_event_header ) + sizeof( uint64_t ) + sizeof( uint32_t );
offset += dataOffset + 8 + 16;
uint32_t pid;
ring.Read( &pid, offset, sizeof( uint32_t ) );
TracyLfqPrepare( QueueType::ThreadWakeup );
MemWrite( &item->threadWakeup.time, t0 );
MemWrite( &item->threadWakeup.thread, pid );
MemWrite( &item->threadWakeup.cpu, (uint8_t)ring.GetCpu() );
int8_t adjustReason = -1; // Does not exist on Linux
int8_t adjustIncrement = 0; // Should perhaps store the new prio?
MemWrite( &item->threadWakeup.adjustReason, adjustReason );
MemWrite( &item->threadWakeup.adjustIncrement, adjustIncrement );
TracyLfqCommit;
}
else

View File

@@ -690,7 +690,9 @@ static pthread_key_t _memory_thread_heap;
# define _Thread_local __declspec(thread)
# define TLS_MODEL
# else
# ifndef __HAIKU__
# if defined(__ANDROID__) && __ANDROID_API__ >= 29 && defined(__NDK_MAJOR__) && __NDK_MAJOR__ >= 26
# define TLS_MODEL __attribute__((tls_model("local-dynamic")))
# elif !defined(__HAIKU__)
# define TLS_MODEL __attribute__((tls_model("initial-exec")))
# else
# define TLS_MODEL
@@ -795,8 +797,7 @@ _rpmalloc_spin(void) {
#elif defined(__sparc__)
__asm__ volatile("rd %ccr, %g0 \n\trd %ccr, %g0 \n\trd %ccr, %g0");
#else
struct timespec ts = {0};
nanosleep(&ts, 0);
std::this_thread::yield();
#endif
}

View File

@@ -9,7 +9,7 @@ namespace tracy
constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; }
enum : uint32_t { ProtocolVersion = 64 };
enum : uint32_t { ProtocolVersion = 74 };
enum : uint16_t { BroadcastVersion = 3 };
using lz4sz_t = uint32_t;
@@ -47,10 +47,10 @@ enum ServerQuery : uint8_t
ServerQueryFrameName,
ServerQueryParameter,
ServerQueryFiberName,
ServerQueryExternalName,
// Items above are high priority. Split order must be preserved. See IsQueryPrio().
ServerQueryDisconnect,
ServerQueryCallstackFrame,
ServerQueryExternalName,
ServerQuerySymbol,
ServerQuerySymbolCode,
ServerQuerySourceCode,

View File

@@ -42,6 +42,8 @@ enum class QueueType : uint8_t
MemAllocCallstackNamed,
MemFreeCallstack,
MemFreeCallstackNamed,
MemDiscard,
MemDiscardCallstack,
GpuZoneBegin,
GpuZoneBeginCallstack,
GpuZoneBeginAllocSrcLoc,
@@ -70,6 +72,7 @@ enum class QueueType : uint8_t
KeepAlive,
ThreadContext,
GpuCalibration,
GpuTimeSync,
Crash,
CrashReport,
ZoneValidation,
@@ -107,6 +110,7 @@ enum class QueueType : uint8_t
SingleStringData,
SecondStringData,
MemNamePayload,
ThreadGroupHint,
StringData,
ThreadName,
PlotName,
@@ -258,6 +262,7 @@ struct QueueFiberEnter
int64_t time;
uint64_t fiber; // ptr
uint32_t thread;
int32_t groupHint;
};
struct QueueFiberLeave
@@ -398,7 +403,10 @@ enum class GpuContextType : uint8_t
Vulkan,
OpenCL,
Direct3D12,
Direct3D11
Direct3D11,
Metal,
Custom,
CUDA
};
enum GpuContextFlags : uint8_t
@@ -453,6 +461,13 @@ struct QueueGpuCalibration
uint8_t context;
};
struct QueueGpuTimeSync
{
int64_t gpuTime;
int64_t cpuTime;
uint8_t context;
};
struct QueueGpuContextName
{
uint8_t context;
@@ -469,6 +484,12 @@ struct QueueMemNamePayload
uint64_t name;
};
struct QueueThreadGroupHint
{
uint32_t thread;
int32_t groupHint;
};
struct QueueMemAlloc
{
int64_t time;
@@ -484,6 +505,13 @@ struct QueueMemFree
uint64_t ptr;
};
struct QueueMemDiscard
{
int64_t time;
uint32_t thread;
uint64_t name;
};
struct QueueCallstackFat
{
uint64_t ptr;
@@ -577,14 +605,20 @@ struct QueueContextSwitch
uint32_t oldThread;
uint32_t newThread;
uint8_t cpu;
uint8_t reason;
uint8_t state;
uint8_t oldThreadWaitReason;
uint8_t oldThreadState;
uint8_t previousCState;
int8_t newThreadPriority;
int8_t oldThreadPriority;
};
struct QueueThreadWakeup
{
int64_t time;
uint32_t thread;
uint8_t cpu;
int8_t adjustReason;
int8_t adjustIncrement;
};
struct QueueTidToPid
@@ -631,6 +665,7 @@ struct QueueSourceCodeNotAvailable
struct QueueCpuTopology
{
uint32_t package;
uint32_t die;
uint32_t core;
uint32_t thread;
};
@@ -718,11 +753,14 @@ struct QueueItem
QueueGpuZoneEnd gpuZoneEnd;
QueueGpuTime gpuTime;
QueueGpuCalibration gpuCalibration;
QueueGpuTimeSync gpuTimeSync;
QueueGpuContextName gpuContextName;
QueueGpuContextNameFat gpuContextNameFat;
QueueMemAlloc memAlloc;
QueueMemFree memFree;
QueueMemDiscard memDiscard;
QueueMemNamePayload memName;
QueueThreadGroupHint threadGroupHint;
QueueCallstackFat callstackFat;
QueueCallstackFatThread callstackFatThread;
QueueCallstackAllocFat callstackAllocFat;
@@ -792,6 +830,8 @@ static constexpr size_t QueueDataSize[] = {
sizeof( QueueHeader ) + sizeof( QueueMemAlloc ), // callstack, named
sizeof( QueueHeader ) + sizeof( QueueMemFree ), // callstack
sizeof( QueueHeader ) + sizeof( QueueMemFree ), // callstack, named
sizeof( QueueHeader ) + sizeof( QueueMemDiscard ),
sizeof( QueueHeader ) + sizeof( QueueMemDiscard ), // callstack
sizeof( QueueHeader ) + sizeof( QueueGpuZoneBegin ),
sizeof( QueueHeader ) + sizeof( QueueGpuZoneBegin ), // callstack
sizeof( QueueHeader ) + sizeof( QueueGpuZoneBeginLean ),// allocated source location
@@ -821,6 +861,7 @@ static constexpr size_t QueueDataSize[] = {
sizeof( QueueHeader ), // keep alive
sizeof( QueueHeader ) + sizeof( QueueThreadContext ),
sizeof( QueueHeader ) + sizeof( QueueGpuCalibration ),
sizeof( QueueHeader ) + sizeof( QueueGpuTimeSync ),
sizeof( QueueHeader ), // crash
sizeof( QueueHeader ) + sizeof( QueueCrashReport ),
sizeof( QueueHeader ) + sizeof( QueueZoneValidation ),
@@ -858,6 +899,7 @@ static constexpr size_t QueueDataSize[] = {
sizeof( QueueHeader ), // single string data
sizeof( QueueHeader ), // second string data
sizeof( QueueHeader ) + sizeof( QueueMemNamePayload ),
sizeof( QueueHeader ) + sizeof( QueueThreadGroupHint ),
// keep all QueueStringTransfer below
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // string data
sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // thread name

View File

@@ -21,6 +21,9 @@
# pragma warning(disable:4267)
# endif
# define poll WSAPoll
# ifdef _MSC_VER
# pragma comment(lib, "ws2_32.lib")
# endif
#else
# include <arpa/inet.h>
# include <sys/socket.h>

View File

@@ -26,8 +26,13 @@
# include <fcntl.h>
#elif defined __FreeBSD__
# include <sys/thr.h>
#elif defined __NetBSD__ || defined __DragonFly__
#elif defined __NetBSD__
# include <lwp.h>
#elif defined __DragonFly__
# include <sys/lwp.h>
#elif defined __QNX__
# include <process.h>
# include <sys/neutrino.h>
#endif
#ifdef __MINGW32__
@@ -82,6 +87,8 @@ TRACY_API uint32_t GetThreadHandleImpl()
return lwp_gettid();
#elif defined __OpenBSD__
return getthrid();
#elif defined __QNX__
return (uint32_t) gettid();
#elif defined __EMSCRIPTEN__
// Not supported, but let it compile.
return 0;
@@ -100,16 +107,10 @@ TRACY_API uint32_t GetThreadHandleImpl()
}
#ifdef TRACY_ENABLE
struct ThreadNameData
{
uint32_t id;
const char* name;
ThreadNameData* next;
};
std::atomic<ThreadNameData*>& GetThreadNameData();
#endif
#ifdef _MSC_VER
#if defined _MSC_VER && !defined __clang__
# pragma pack( push, 8 )
struct THREADNAME_INFO
{
@@ -133,6 +134,11 @@ void ThreadNameMsvcMagic( const THREADNAME_INFO& info )
#endif
TRACY_API void SetThreadName( const char* name )
{
SetThreadNameWithHint( name, 0 );
}
TRACY_API void SetThreadNameWithHint( const char* name, int32_t groupHint )
{
#if defined _WIN32
# ifdef TRACY_UWP
@@ -148,7 +154,7 @@ TRACY_API void SetThreadName( const char* name )
}
else
{
# if defined _MSC_VER
# if defined _MSC_VER && !defined __clang__
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
@@ -180,6 +186,21 @@ TRACY_API void SetThreadName( const char* name )
#endif
}
}
#elif defined __QNX__
{
const auto sz = strlen( name );
if( sz <= _NTO_THREAD_NAME_MAX )
{
pthread_setname_np( pthread_self(), name );
}
else
{
char buf[_NTO_THREAD_NAME_MAX + 1];
memcpy( buf, name, _NTO_THREAD_NAME_MAX );
buf[_NTO_THREAD_NAME_MAX] = '\0';
pthread_setname_np( pthread_self(), buf );
}
};
#endif
#ifdef TRACY_ENABLE
{
@@ -189,6 +210,7 @@ TRACY_API void SetThreadName( const char* name )
buf[sz] = '\0';
auto data = (ThreadNameData*)tracy_malloc_fast( sizeof( ThreadNameData ) );
data->id = detail::GetThreadHandleImpl();
data->groupHint = groupHint;
data->name = buf;
data->next = GetThreadNameData().load( std::memory_order_relaxed );
while( !GetThreadNameData().compare_exchange_weak( data->next, data, std::memory_order_release, std::memory_order_relaxed ) ) {}
@@ -196,6 +218,22 @@ TRACY_API void SetThreadName( const char* name )
#endif
}
#ifdef TRACY_ENABLE
ThreadNameData* GetThreadNameData( uint32_t id )
{
auto ptr = GetThreadNameData().load( std::memory_order_relaxed );
while( ptr )
{
if( ptr->id == id )
{
return ptr;
}
ptr = ptr->next;
}
return nullptr;
}
#endif
TRACY_API const char* GetThreadName( uint32_t id )
{
static char buf[256];
@@ -259,6 +297,11 @@ TRACY_API const char* GetThreadName( uint32_t id )
pthread_setcancelstate( cs, 0 );
# endif
return buf;
#elif defined __QNX__
static char qnxNameBuf[_NTO_THREAD_NAME_MAX + 1] = {0};
if (pthread_getname_np(static_cast<int>(id), qnxNameBuf, _NTO_THREAD_NAME_MAX) == 0) {
return qnxNameBuf;
};
#endif
sprintf( buf, "%" PRIu32, id );

View File

@@ -47,17 +47,18 @@ public:
ScopedZone& operator=( const ScopedZone& ) = delete;
ScopedZone& operator=( ScopedZone&& ) = delete;
ScopedZone( const SourceLocationData* srcloc, bool is_active = true );
ScopedZone( const SourceLocationData* srcloc, int depth, bool is_active = true );
ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, bool is_active = true );
ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int depth, bool is_active = true );
ScopedZone( const SourceLocationData* srcloc, int32_t depth = -1, bool is_active = true );
ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, uint32_t color, int32_t depth = -1, bool is_active = true );
ScopedZone( uint32_t line, const char* source, size_t sourceSz, const char* function, size_t functionSz, const char* name, size_t nameSz, int32_t depth, bool is_active = true );
~ScopedZone();
void Text( const char* txt, size_t size );
void Text( const Char* txt, size_t size );
void TextFmt( const char* fmt, ... );
void Name( const char* txt, size_t size );
void Name( const Char* txt, size_t size );
void NameFmt( const char* fmt, ... );
void Color( uint32_t color );
void Value( uint64_t value );
@@ -75,6 +76,16 @@ TRACY_API uint32_t GetThreadHandleImpl();
}
#ifdef TRACY_ENABLE
struct ThreadNameData
{
uint32_t id;
int32_t groupHint;
const char* name;
ThreadNameData* next;
};
ThreadNameData* GetThreadNameData( uint32_t id );
TRACY_API uint32_t GetThreadHandle();
#else
static inline uint32_t GetThreadHandle()
@@ -84,9 +95,10 @@ static inline uint32_t GetThreadHandle()
#endif
TRACY_API void SetThreadName( const char* name );
TRACY_API void SetThreadNameWithHint( const char* name, int32_t groupHint );
TRACY_API const char* GetThreadName( uint32_t id );
TRACY_API const char* GetEnvVar(const char* name);
TRACY_API const char* GetEnvVar( const char* name );
}

View File

@@ -6,7 +6,7 @@ namespace tracy
namespace Version
{
enum { Major = 0 };
enum { Minor = 10 };
enum { Minor = 12 };
enum { Patch = 0 };
}
}

View File

@@ -725,8 +725,8 @@ struct dwarf_data
struct dwarf_data *next;
/* The data for .gnu_debugaltlink. */
struct dwarf_data *altlink;
/* The base address for this file. */
uintptr_t base_address;
/* The base address mapping for this file. */
struct libbacktrace_base_address base_address;
/* A sorted list of address ranges. */
struct unit_addrs *addrs;
/* Number of address ranges in list. */
@@ -1947,8 +1947,9 @@ update_pcrange (const struct attr* attr, const struct attr_val* val,
static int
add_low_high_range (struct backtrace_state *state,
const struct dwarf_sections *dwarf_sections,
uintptr_t base_address, int is_bigendian,
struct unit *u, const struct pcrange *pcrange,
struct libbacktrace_base_address base_address,
int is_bigendian, struct unit *u,
const struct pcrange *pcrange,
int (*add_range) (struct backtrace_state *state,
void *rdata, uintptr_t lowpc,
uintptr_t highpc,
@@ -1983,8 +1984,8 @@ add_low_high_range (struct backtrace_state *state,
/* Add in the base address of the module when recording PC values,
so that we can look up the PC directly. */
lowpc += base_address;
highpc += base_address;
lowpc = libbacktrace_add_base (lowpc, base_address);
highpc = libbacktrace_add_base (highpc, base_address);
return add_range (state, rdata, lowpc, highpc, error_callback, data, vec);
}
@@ -1996,7 +1997,7 @@ static int
add_ranges_from_ranges (
struct backtrace_state *state,
const struct dwarf_sections *dwarf_sections,
uintptr_t base_address, int is_bigendian,
struct libbacktrace_base_address base_address, int is_bigendian,
struct unit *u, uintptr_t base,
const struct pcrange *pcrange,
int (*add_range) (struct backtrace_state *state, void *rdata,
@@ -2042,10 +2043,11 @@ add_ranges_from_ranges (
base = (uintptr_t) high;
else
{
if (!add_range (state, rdata,
(uintptr_t) low + base + base_address,
(uintptr_t) high + base + base_address,
error_callback, data, vec))
uintptr_t rl, rh;
rl = libbacktrace_add_base ((uintptr_t) low + base, base_address);
rh = libbacktrace_add_base ((uintptr_t) high + base, base_address);
if (!add_range (state, rdata, rl, rh, error_callback, data, vec))
return 0;
}
}
@@ -2063,7 +2065,7 @@ static int
add_ranges_from_rnglists (
struct backtrace_state *state,
const struct dwarf_sections *dwarf_sections,
uintptr_t base_address, int is_bigendian,
struct libbacktrace_base_address base_address, int is_bigendian,
struct unit *u, uintptr_t base,
const struct pcrange *pcrange,
int (*add_range) (struct backtrace_state *state, void *rdata,
@@ -2146,9 +2148,10 @@ add_ranges_from_rnglists (
u->addrsize, is_bigendian, index,
error_callback, data, &high))
return 0;
if (!add_range (state, rdata, low + base_address,
high + base_address, error_callback, data,
vec))
if (!add_range (state, rdata,
libbacktrace_add_base (low, base_address),
libbacktrace_add_base (high, base_address),
error_callback, data, vec))
return 0;
}
break;
@@ -2165,7 +2168,7 @@ add_ranges_from_rnglists (
error_callback, data, &low))
return 0;
length = read_uleb128 (&rnglists_buf);
low += base_address;
low = libbacktrace_add_base (low, base_address);
if (!add_range (state, rdata, low, low + length,
error_callback, data, vec))
return 0;
@@ -2179,8 +2182,9 @@ add_ranges_from_rnglists (
low = read_uleb128 (&rnglists_buf);
high = read_uleb128 (&rnglists_buf);
if (!add_range (state, rdata, low + base + base_address,
high + base + base_address,
if (!add_range (state, rdata,
libbacktrace_add_base (low + base, base_address),
libbacktrace_add_base (high + base, base_address),
error_callback, data, vec))
return 0;
}
@@ -2197,9 +2201,10 @@ add_ranges_from_rnglists (
low = (uintptr_t) read_address (&rnglists_buf, u->addrsize);
high = (uintptr_t) read_address (&rnglists_buf, u->addrsize);
if (!add_range (state, rdata, low + base_address,
high + base_address, error_callback, data,
vec))
if (!add_range (state, rdata,
libbacktrace_add_base (low, base_address),
libbacktrace_add_base (high, base_address),
error_callback, data, vec))
return 0;
}
break;
@@ -2211,7 +2216,7 @@ add_ranges_from_rnglists (
low = (uintptr_t) read_address (&rnglists_buf, u->addrsize);
length = (uintptr_t) read_uleb128 (&rnglists_buf);
low += base_address;
low = libbacktrace_add_base (low, base_address);
if (!add_range (state, rdata, low, low + length,
error_callback, data, vec))
return 0;
@@ -2239,7 +2244,7 @@ add_ranges_from_rnglists (
static int
add_ranges (struct backtrace_state *state,
const struct dwarf_sections *dwarf_sections,
uintptr_t base_address, int is_bigendian,
struct libbacktrace_base_address base_address, int is_bigendian,
struct unit *u, uintptr_t base, const struct pcrange *pcrange,
int (*add_range) (struct backtrace_state *state, void *rdata,
uintptr_t lowpc, uintptr_t highpc,
@@ -2275,7 +2280,8 @@ add_ranges (struct backtrace_state *state,
read, 0 if there is some error. */
static int
find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
find_address_ranges (struct backtrace_state *state,
struct libbacktrace_base_address base_address,
struct dwarf_buf *unit_buf,
const struct dwarf_sections *dwarf_sections,
int is_bigendian, struct dwarf_data *altlink,
@@ -2430,7 +2436,8 @@ find_address_ranges (struct backtrace_state *state, uintptr_t base_address,
on success, 0 on failure. */
static int
build_address_map (struct backtrace_state *state, uintptr_t base_address,
build_address_map (struct backtrace_state *state,
struct libbacktrace_base_address base_address,
const struct dwarf_sections *dwarf_sections,
int is_bigendian, struct dwarf_data *altlink,
backtrace_error_callback error_callback, void *data,
@@ -2649,7 +2656,7 @@ add_line (struct backtrace_state *state, struct dwarf_data *ddata,
/* Add in the base address here, so that we can look up the PC
directly. */
ln->pc = pc + ddata->base_address;
ln->pc = libbacktrace_add_base (pc, ddata->base_address);
ln->filename = filename;
ln->lineno = lineno;
@@ -4251,6 +4258,19 @@ dwarf_lookup_pc (struct backtrace_state *state, struct dwarf_data *ddata,
}
}
bool dwarf_fileline_dwarf_lookup_pc_in_all_entries(struct backtrace_state *state, uintptr_t pc,
backtrace_full_callback callback, backtrace_error_callback error_callback, void *data,
int& found, int ret)
{
for (struct dwarf_data* ddata = (struct dwarf_data *)state->fileline_data;
ddata != NULL;
ddata = ddata->next)
{
ret = dwarf_lookup_pc(state, ddata, pc, callback, error_callback, data, &found);
if (ret != 0 || found) return true;
}
return false;
}
/* Return the file/line information for a PC using the DWARF mapping
we built earlier. */
@@ -4262,20 +4282,30 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
{
struct dwarf_data *ddata;
int found;
int ret;
int ret = 0;
if (!state->threaded)
{
if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret))
{
for (ddata = (struct dwarf_data *) state->fileline_data;
ddata != NULL;
ddata = ddata->next)
{
ret = dwarf_lookup_pc (state, ddata, pc, callback, error_callback,
data, &found);
if (ret != 0 || found)
return ret;
}
return ret;
}
// if we failed to obtain an entry in range, it can mean that the address map has been changed and new entries
// have been loaded in the meantime. Request a refresh and try again.
if (state->request_known_address_ranges_refresh_fn)
{
int new_range_count = state->request_known_address_ranges_refresh_fn(state, pc);
if (new_range_count > 0)
{
if (dwarf_fileline_dwarf_lookup_pc_in_all_entries(state, pc, callback, error_callback, data, found, ret))
{
return ret;
}
}
}
}
else
{
struct dwarf_data **pp;
@@ -4306,7 +4336,7 @@ dwarf_fileline (struct backtrace_state *state, uintptr_t pc,
static struct dwarf_data *
build_dwarf_data (struct backtrace_state *state,
uintptr_t base_address,
struct libbacktrace_base_address base_address,
const struct dwarf_sections *dwarf_sections,
int is_bigendian,
struct dwarf_data *altlink,
@@ -4364,7 +4394,7 @@ build_dwarf_data (struct backtrace_state *state,
int
backtrace_dwarf_add (struct backtrace_state *state,
uintptr_t base_address,
struct libbacktrace_base_address base_address,
const struct dwarf_sections *dwarf_sections,
int is_bigendian,
struct dwarf_data *fileline_altlink,

View File

@@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE. */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#ifdef HAVE_DL_ITERATE_PHDR
#include <link.h>
@@ -642,7 +643,7 @@ elf_symbol_search (const void *vkey, const void *ventry)
static int
elf_initialize_syminfo (struct backtrace_state *state,
uintptr_t base_address,
struct libbacktrace_base_address base_address,
const unsigned char *symtab_data, size_t symtab_size,
const unsigned char *strtab, size_t strtab_size,
backtrace_error_callback error_callback,
@@ -708,7 +709,8 @@ elf_initialize_syminfo (struct backtrace_state *state,
= *(const b_elf_addr *) (opd->data + (sym->st_value - opd->addr));
else
elf_symbols[j].address = sym->st_value;
elf_symbols[j].address += base_address;
elf_symbols[j].address =
libbacktrace_add_base (elf_symbols[j].address, base_address);
elf_symbols[j].size = sym->st_size;
++j;
}
@@ -1199,14 +1201,7 @@ elf_fetch_bits_backward (const unsigned char **ppin,
val = *pval;
if (unlikely (pin <= pinend))
{
if (bits == 0)
{
elf_uncompress_failed ();
return 0;
}
return 1;
}
return 1;
pin -= 4;
@@ -5093,7 +5088,7 @@ elf_uncompress_chdr (struct backtrace_state *state,
backtrace_error_callback error_callback, void *data,
unsigned char **uncompressed, size_t *uncompressed_size)
{
const b_elf_chdr *chdr;
b_elf_chdr chdr;
char *alc;
size_t alc_len;
unsigned char *po;
@@ -5105,27 +5100,30 @@ elf_uncompress_chdr (struct backtrace_state *state,
if (compressed_size < sizeof (b_elf_chdr))
return 1;
chdr = (const b_elf_chdr *) compressed;
/* The lld linker can misalign a compressed section, so we can't safely read
the fields directly as we can for other ELF sections. See
https://github.com/ianlancetaylor/libbacktrace/pull/120. */
memcpy (&chdr, compressed, sizeof (b_elf_chdr));
alc = NULL;
alc_len = 0;
if (*uncompressed != NULL && *uncompressed_size >= chdr->ch_size)
if (*uncompressed != NULL && *uncompressed_size >= chdr.ch_size)
po = *uncompressed;
else
{
alc_len = chdr->ch_size;
alc_len = chdr.ch_size;
alc = (char*)backtrace_alloc (state, alc_len, error_callback, data);
if (alc == NULL)
return 0;
po = (unsigned char *) alc;
}
switch (chdr->ch_type)
switch (chdr.ch_type)
{
case ELFCOMPRESS_ZLIB:
if (!elf_zlib_inflate_and_verify (compressed + sizeof (b_elf_chdr),
compressed_size - sizeof (b_elf_chdr),
zdebug_table, po, chdr->ch_size))
zdebug_table, po, chdr.ch_size))
goto skip;
break;
@@ -5133,7 +5131,7 @@ elf_uncompress_chdr (struct backtrace_state *state,
if (!elf_zstd_decompress (compressed + sizeof (b_elf_chdr),
compressed_size - sizeof (b_elf_chdr),
(unsigned char *)zdebug_table, po,
chdr->ch_size))
chdr.ch_size))
goto skip;
break;
@@ -5143,7 +5141,7 @@ elf_uncompress_chdr (struct backtrace_state *state,
}
*uncompressed = po;
*uncompressed_size = chdr->ch_size;
*uncompressed_size = chdr.ch_size;
return 1;
@@ -5585,6 +5583,7 @@ elf_uncompress_lzma_block (const unsigned char *compressed,
uint64_t header_compressed_size;
uint64_t header_uncompressed_size;
unsigned char lzma2_properties;
size_t crc_offset;
uint32_t computed_crc;
uint32_t stream_crc;
size_t uncompressed_offset;
@@ -5688,28 +5687,29 @@ elf_uncompress_lzma_block (const unsigned char *compressed,
/* The properties describe the dictionary size, but we don't care
what that is. */
/* Block header padding. */
if (unlikely (off + 4 > compressed_size))
/* Skip to just before CRC, verifying zero bytes in between. */
crc_offset = block_header_offset + block_header_size - 4;
if (unlikely (crc_offset + 4 > compressed_size))
{
elf_uncompress_failed ();
return 0;
}
off = (off + 3) &~ (size_t) 3;
if (unlikely (off + 4 > compressed_size))
for (; off < crc_offset; off++)
{
elf_uncompress_failed ();
return 0;
if (compressed[off] != 0)
{
elf_uncompress_failed ();
return 0;
}
}
/* Block header CRC. */
computed_crc = elf_crc32 (0, compressed + block_header_offset,
block_header_size - 4);
stream_crc = (compressed[off]
| (compressed[off + 1] << 8)
| (compressed[off + 2] << 16)
| (compressed[off + 3] << 24));
stream_crc = ((uint32_t)compressed[off]
| ((uint32_t)compressed[off + 1] << 8)
| ((uint32_t)compressed[off + 2] << 16)
| ((uint32_t)compressed[off + 3] << 24));
if (unlikely (computed_crc != stream_crc))
{
elf_uncompress_failed ();
@@ -6216,10 +6216,10 @@ elf_uncompress_lzma_block (const unsigned char *compressed,
return 0;
}
computed_crc = elf_crc32 (0, uncompressed, uncompressed_offset);
stream_crc = (compressed[off]
| (compressed[off + 1] << 8)
| (compressed[off + 2] << 16)
| (compressed[off + 3] << 24));
stream_crc = ((uint32_t)compressed[off]
| ((uint32_t)compressed[off + 1] << 8)
| ((uint32_t)compressed[off + 2] << 16)
| ((uint32_t)compressed[off + 3] << 24));
if (computed_crc != stream_crc)
{
elf_uncompress_failed ();
@@ -6319,10 +6319,10 @@ elf_uncompress_lzma (struct backtrace_state *state,
/* Next comes a CRC of the stream flags. */
computed_crc = elf_crc32 (0, compressed + 6, 2);
stream_crc = (compressed[8]
| (compressed[9] << 8)
| (compressed[10] << 16)
| (compressed[11] << 24));
stream_crc = ((uint32_t)compressed[8]
| ((uint32_t)compressed[9] << 8)
| ((uint32_t)compressed[10] << 16)
| ((uint32_t)compressed[11] << 24));
if (unlikely (computed_crc != stream_crc))
{
elf_uncompress_failed ();
@@ -6363,10 +6363,10 @@ elf_uncompress_lzma (struct backtrace_state *state,
/* Before that is a footer CRC. */
computed_crc = elf_crc32 (0, compressed + offset, 6);
stream_crc = (compressed[offset - 4]
| (compressed[offset - 3] << 8)
| (compressed[offset - 2] << 16)
| (compressed[offset - 1] << 24));
stream_crc = ((uint32_t)compressed[offset - 4]
| ((uint32_t)compressed[offset - 3] << 8)
| ((uint32_t)compressed[offset - 2] << 16)
| ((uint32_t)compressed[offset - 1] << 24));
if (unlikely (computed_crc != stream_crc))
{
elf_uncompress_failed ();
@@ -6422,10 +6422,10 @@ elf_uncompress_lzma (struct backtrace_state *state,
/* Next is a CRC of the index. */
computed_crc = elf_crc32 (0, compressed + index_offset,
offset - index_offset);
stream_crc = (compressed[offset]
| (compressed[offset + 1] << 8)
| (compressed[offset + 2] << 16)
| (compressed[offset + 3] << 24));
stream_crc = ((uint32_t)compressed[offset]
| ((uint32_t)compressed[offset + 1] << 8)
| ((uint32_t)compressed[offset + 2] << 16)
| ((uint32_t)compressed[offset + 3] << 24));
if (unlikely (computed_crc != stream_crc))
{
elf_uncompress_failed ();
@@ -6518,8 +6518,10 @@ backtrace_uncompress_lzma (struct backtrace_state *state,
static int
elf_add (struct backtrace_state *state, const char *filename, int descriptor,
const unsigned char *memory, size_t memory_size,
uintptr_t base_address, backtrace_error_callback error_callback,
void *data, fileline *fileline_fn, int *found_sym, int *found_dwarf,
struct libbacktrace_base_address base_address,
struct elf_ppc64_opd_data *caller_opd,
backtrace_error_callback error_callback, void *data,
fileline *fileline_fn, int *found_sym, int *found_dwarf,
struct dwarf_data **fileline_entry, int exe, int debuginfo,
const char *with_buildid_data, uint32_t with_buildid_size)
{
@@ -6574,6 +6576,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
struct elf_view split_debug_view[DEBUG_MAX];
unsigned char split_debug_view_valid[DEBUG_MAX];
struct elf_ppc64_opd_data opd_data, *opd;
int opd_view_valid;
struct dwarf_sections dwarf_sections;
struct dwarf_data *fileline_altlink = NULL;
@@ -6602,6 +6605,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
debug_view_valid = 0;
memset (&split_debug_view_valid[0], 0, sizeof split_debug_view_valid);
opd = NULL;
opd_view_valid = 0;
if (!elf_get_view (state, descriptor, memory, memory_size, 0, sizeof ehdr,
error_callback, data, &ehdr_view))
@@ -6858,7 +6862,8 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
}
}
if (!gnu_debugdata_view_valid
if (!debuginfo
&& !gnu_debugdata_view_valid
&& strcmp (name, ".gnu_debugdata") == 0)
{
if (!elf_get_view (state, descriptor, memory, memory_size,
@@ -6885,12 +6890,18 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
opd->addr = shdr->sh_addr;
opd->data = (const char *) opd_data.view.view.data;
opd->size = shdr->sh_size;
opd_view_valid = 1;
}
}
/* A debuginfo file may not have a useful .opd section, but we can use the
one from the original executable. */
if (opd == NULL)
opd = caller_opd;
if (symtab_shndx == 0)
symtab_shndx = dynsym_shndx;
if (symtab_shndx != 0 && !debuginfo)
if (symtab_shndx != 0)
{
const b_elf_shdr *symtab_shdr;
unsigned int strtab_shndx;
@@ -6966,9 +6977,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
elf_release_view (state, &debuglink_view, error_callback, data);
if (debugaltlink_view_valid)
elf_release_view (state, &debugaltlink_view, error_callback, data);
ret = elf_add (state, "", d, NULL, 0, base_address, error_callback,
data, fileline_fn, found_sym, found_dwarf, NULL, 0,
1, NULL, 0);
ret = elf_add (state, "", d, NULL, 0, base_address, opd,
error_callback, data, fileline_fn, found_sym,
found_dwarf, NULL, 0, 1, NULL, 0);
if (ret < 0)
backtrace_close (d, error_callback, data);
else if (descriptor >= 0)
@@ -6983,12 +6994,6 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
buildid_view_valid = 0;
}
if (opd)
{
elf_release_view (state, &opd->view, error_callback, data);
opd = NULL;
}
if (debuglink_name != NULL)
{
int d;
@@ -7003,9 +7008,9 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
elf_release_view (state, &debuglink_view, error_callback, data);
if (debugaltlink_view_valid)
elf_release_view (state, &debugaltlink_view, error_callback, data);
ret = elf_add (state, "", d, NULL, 0, base_address, error_callback,
data, fileline_fn, found_sym, found_dwarf, NULL, 0,
1, NULL, 0);
ret = elf_add (state, "", d, NULL, 0, base_address, opd,
error_callback, data, fileline_fn, found_sym,
found_dwarf, NULL, 0, 1, NULL, 0);
if (ret < 0)
backtrace_close (d, error_callback, data);
else if (descriptor >= 0)
@@ -7030,7 +7035,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
{
int ret;
ret = elf_add (state, filename, d, NULL, 0, base_address,
ret = elf_add (state, filename, d, NULL, 0, base_address, opd,
error_callback, data, fileline_fn, found_sym,
found_dwarf, &fileline_altlink, 0, 1,
debugaltlink_buildid_data, debugaltlink_buildid_size);
@@ -7067,7 +7072,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
if (ret)
{
ret = elf_add (state, filename, -1, gnu_debugdata_uncompressed,
gnu_debugdata_uncompressed_size, base_address,
gnu_debugdata_uncompressed_size, base_address, opd,
error_callback, data, fileline_fn, found_sym,
found_dwarf, NULL, 0, 0, NULL, 0);
if (ret >= 0 && descriptor >= 0)
@@ -7076,6 +7081,13 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
}
}
if (opd_view_valid)
{
elf_release_view (state, &opd->view, error_callback, data);
opd_view_valid = 0;
opd = NULL;
}
/* Read all the debug sections in a single view, since they are
probably adjacent in the file. If any of sections are
uncompressed, we never release this view. */
@@ -7322,7 +7334,7 @@ elf_add (struct backtrace_state *state, const char *filename, int descriptor,
if (split_debug_view_valid[i])
elf_release_view (state, &split_debug_view[i], error_callback, data);
}
if (opd)
if (opd_view_valid)
elf_release_view (state, &opd->view, error_callback, data);
if (descriptor >= 0)
backtrace_close (descriptor, error_callback, data);
@@ -7350,13 +7362,37 @@ struct PhdrIterate
{
char* dlpi_name;
ElfW(Addr) dlpi_addr;
ElfW(Addr) dlpi_end_addr;
};
FastVector<PhdrIterate> s_phdrData(16);
struct ElfAddrRange
{
ElfW(Addr) dlpi_addr;
ElfW(Addr) dlpi_end_addr;
};
FastVector<ElfAddrRange> s_sortedKnownElfRanges(16);
static int address_in_known_elf_ranges(uintptr_t pc)
{
auto it = std::lower_bound( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(), pc,
[]( const ElfAddrRange& lhs, const uintptr_t rhs ) { return uintptr_t(lhs.dlpi_addr) > rhs; } );
if( it != s_sortedKnownElfRanges.end() && pc <= it->dlpi_end_addr )
{
return true;
}
return false;
}
static int
phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
void *pdata)
{
if( address_in_known_elf_ranges(info->dlpi_addr) )
{
return 0;
}
auto ptr = s_phdrData.push_next();
if (info->dlpi_name)
{
@@ -7366,6 +7402,12 @@ phdr_callback_mock (struct dl_phdr_info *info, size_t size ATTRIBUTE_UNUSED,
}
else ptr->dlpi_name = nullptr;
ptr->dlpi_addr = info->dlpi_addr;
// calculate the end address as well, so we can quickly determine if a PC is within the range of this image
ptr->dlpi_end_addr = uintptr_t(info->dlpi_addr) + (info->dlpi_phnum ? uintptr_t(
info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr +
info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz) : 0);
return 0;
}
@@ -7379,6 +7421,7 @@ phdr_callback (struct PhdrIterate *info, void *pdata)
const char *filename;
int descriptor;
int does_not_exist;
struct libbacktrace_base_address base_address;
fileline elf_fileline_fn;
int found_dwarf;
@@ -7408,7 +7451,8 @@ phdr_callback (struct PhdrIterate *info, void *pdata)
return 0;
}
if (elf_add (pd->state, filename, descriptor, NULL, 0, info->dlpi_addr,
base_address.m = info->dlpi_addr;
if (elf_add (pd->state, filename, descriptor, NULL, 0, base_address, NULL,
pd->error_callback, pd->data, &elf_fileline_fn, pd->found_sym,
&found_dwarf, NULL, 0, 0, NULL, 0))
{
@@ -7422,6 +7466,66 @@ phdr_callback (struct PhdrIterate *info, void *pdata)
return 0;
}
static int elf_iterate_phdr_and_add_new_files(phdr_data *pd)
{
assert(s_phdrData.empty());
// dl_iterate_phdr, will only add entries for elf files loaded in a previously unseen range
dl_iterate_phdr(phdr_callback_mock, nullptr);
if(s_phdrData.size() == 0)
{
return 0;
}
uint32_t headersAdded = 0;
for (auto &v : s_phdrData)
{
phdr_callback(&v, (void *)pd);
auto newEntry = s_sortedKnownElfRanges.push_next();
newEntry->dlpi_addr = v.dlpi_addr;
newEntry->dlpi_end_addr = v.dlpi_end_addr;
tracy_free(v.dlpi_name);
headersAdded++;
}
s_phdrData.clear();
std::sort( s_sortedKnownElfRanges.begin(), s_sortedKnownElfRanges.end(),
[]( const ElfAddrRange& lhs, const ElfAddrRange& rhs ) { return lhs.dlpi_addr > rhs.dlpi_addr; } );
return headersAdded;
}
#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
/* Request an elf entry update if the pc passed in is not in any of the known elf ranges.
This could mean that new images were dlopened and we need to add those new elf entries */
static int elf_refresh_address_ranges_if_needed(struct backtrace_state *state, uintptr_t pc)
{
if ( address_in_known_elf_ranges(pc) )
{
return 0;
}
struct phdr_data pd;
int found_sym = 0;
int found_dwarf = 0;
fileline fileline_fn = nullptr;
pd.state = state;
pd.error_callback = nullptr;
pd.data = nullptr;
pd.fileline_fn = &fileline_fn;
pd.found_sym = &found_sym;
pd.found_dwarf = &found_dwarf;
pd.exe_filename = nullptr;
pd.exe_descriptor = -1;
return elf_iterate_phdr_and_add_new_files(&pd);
}
#endif //#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
/* Initialize the backtrace data we need from an ELF executable. At
the ELF level, all we need to do is find the debug info
sections. */
@@ -7437,11 +7541,21 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
fileline elf_fileline_fn = elf_nodebug;
struct phdr_data pd;
ret = elf_add (state, filename, descriptor, NULL, 0, 0, error_callback, data,
&elf_fileline_fn, &found_sym, &found_dwarf, NULL, 1, 0, NULL,
0);
if (!ret)
return 0;
/* When using fdpic we must use dl_iterate_phdr for all modules, including
the main executable, so that we can get the right base address
mapping. */
if (!libbacktrace_using_fdpic ())
{
struct libbacktrace_base_address zero_base_address;
memset (&zero_base_address, 0, sizeof zero_base_address);
ret = elf_add (state, filename, descriptor, NULL, 0, zero_base_address,
NULL, error_callback, data, &elf_fileline_fn, &found_sym,
&found_dwarf, NULL, 1, 0, NULL, 0);
if (!ret)
return 0;
}
pd.state = state;
pd.error_callback = error_callback;
@@ -7452,14 +7566,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
pd.exe_filename = filename;
pd.exe_descriptor = ret < 0 ? descriptor : -1;
assert (s_phdrData.empty());
dl_iterate_phdr (phdr_callback_mock, nullptr);
for (auto& v : s_phdrData)
{
phdr_callback (&v, (void *) &pd);
tracy_free (v.dlpi_name);
}
s_phdrData.clear();
elf_iterate_phdr_and_add_new_files(&pd);
if (!state->threaded)
{
@@ -7485,6 +7592,13 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
if (*fileline_fn == NULL || *fileline_fn == elf_nodebug)
*fileline_fn = elf_fileline_fn;
// install an address range refresh callback so we can cope with dynamically loaded elf files
#ifdef TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT
state->request_known_address_ranges_refresh_fn = elf_refresh_address_ranges_if_needed;
#else
state->request_known_address_ranges_refresh_fn = NULL;
#endif
return 1;
}

View File

@@ -47,6 +47,18 @@ POSSIBILITY OF SUCH DAMAGE. */
#include <mach-o/dyld.h>
#endif
#ifdef HAVE_WINDOWS_H
#ifndef WIN32_MEAN_AND_LEAN
#define WIN32_MEAN_AND_LEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#endif
#include "backtrace.hpp"
#include "internal.hpp"
@@ -158,6 +170,47 @@ macho_get_executable_path (struct backtrace_state *state,
#endif /* !defined (HAVE_MACH_O_DYLD_H) */
#if HAVE_DECL__PGMPTR
#define windows_executable_filename() _pgmptr
#else /* !HAVE_DECL__PGMPTR */
#define windows_executable_filename() NULL
#endif /* !HAVE_DECL__PGMPTR */
#ifdef HAVE_WINDOWS_H
#define FILENAME_BUF_SIZE (MAX_PATH)
static char *
windows_get_executable_path (char *buf, backtrace_error_callback error_callback,
void *data)
{
size_t got;
int error;
got = GetModuleFileNameA (NULL, buf, FILENAME_BUF_SIZE - 1);
error = GetLastError ();
if (got == 0
|| (got == FILENAME_BUF_SIZE - 1 && error == ERROR_INSUFFICIENT_BUFFER))
{
error_callback (data,
"could not get the filename of the current executable",
error);
return NULL;
}
return buf;
}
#else /* !defined (HAVE_WINDOWS_H) */
#define windows_get_executable_path(buf, error_callback, data) NULL
#define FILENAME_BUF_SIZE 64
#endif /* !defined (HAVE_WINDOWS_H) */
/* Initialize the fileline information from the executable. Returns 1
on success, 0 on failure. */
@@ -171,7 +224,7 @@ fileline_initialize (struct backtrace_state *state,
int called_error_callback;
int descriptor;
const char *filename;
char buf[64];
char buf[FILENAME_BUF_SIZE];
if (!state->threaded)
failed = state->fileline_initialization_failed;
@@ -195,7 +248,7 @@ fileline_initialize (struct backtrace_state *state,
descriptor = -1;
called_error_callback = 0;
for (pass = 0; pass < 8; ++pass)
for (pass = 0; pass < 10; ++pass)
{
int does_not_exist;
@@ -208,25 +261,33 @@ fileline_initialize (struct backtrace_state *state,
filename = getexecname ();
break;
case 2:
filename = "/proc/self/exe";
/* Test this before /proc/self/exe, as the latter exists but points
to the wine binary (and thus doesn't work). */
filename = windows_executable_filename ();
break;
case 3:
filename = "/proc/curproc/file";
filename = "/proc/self/exe";
break;
case 4:
filename = "/proc/curproc/file";
break;
case 5:
snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
(long) getpid ());
filename = buf;
break;
case 5:
case 6:
filename = sysctl_exec_name1 (state, error_callback, data);
break;
case 6:
case 7:
filename = sysctl_exec_name2 (state, error_callback, data);
break;
case 7:
case 8:
filename = macho_get_executable_path (state, error_callback, data);
break;
case 9:
filename = windows_get_executable_path (buf, error_callback, data);
break;
default:
abort ();
}

View File

@@ -133,6 +133,11 @@ typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc,
backtrace_syminfo_callback callback,
backtrace_error_callback error_callback, void *data);
/* The type of the function that will trigger an known address range refresh
(if pc passed in is for an address whichs lies ourtisde of known ranges) */
typedef int (*request_known_address_ranges_refresh)(struct backtrace_state *state,
uintptr_t pc);
/* What the backtrace state pointer points to. */
struct backtrace_state
@@ -159,6 +164,8 @@ struct backtrace_state
int lock_alloc;
/* The freelist when using mmap. */
struct backtrace_freelist_struct *freelist;
/* Trigger an known address range refresh */
request_known_address_ranges_refresh request_known_address_ranges_refresh_fn;
};
/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
@@ -326,10 +333,44 @@ struct dwarf_sections
struct dwarf_data;
/* The load address mapping. */
#if defined(__FDPIC__) && defined(HAVE_DL_ITERATE_PHDR) && (defined(HAVE_LINK_H) || defined(HAVE_SYS_LINK_H))
#ifdef HAVE_LINK_H
#include <link.h>
#endif
#ifdef HAVE_SYS_LINK_H
#include <sys/link.h>
#endif
#define libbacktrace_using_fdpic() (1)
struct libbacktrace_base_address
{
struct elf32_fdpic_loadaddr m;
};
#define libbacktrace_add_base(pc, base) \
((uintptr_t) (__RELOC_POINTER ((pc), (base).m)))
#else /* not _FDPIC__ */
#define libbacktrace_using_fdpic() (0)
struct libbacktrace_base_address
{
uintptr_t m;
};
#define libbacktrace_add_base(pc, base) ((pc) + (base).m)
#endif /* not _FDPIC__ */
/* Add file/line information for a DWARF module. */
extern int backtrace_dwarf_add (struct backtrace_state *state,
uintptr_t base_address,
struct libbacktrace_base_address base_address,
const struct dwarf_sections *dwarf_sections,
int is_bigendian,
struct dwarf_data *fileline_altlink,

View File

@@ -274,12 +274,14 @@ struct macho_nlist_64
/* Value found in nlist n_type field. */
#define MACH_O_N_EXT 0x01 /* Extern symbol */
#define MACH_O_N_ABS 0x02 /* Absolute symbol */
#define MACH_O_N_SECT 0x0e /* Defined in section */
#define MACH_O_N_TYPE 0x0e /* Mask for type bits */
#define MACH_O_N_STAB 0xe0 /* Stabs debugging symbol */
#define MACH_O_N_TYPE 0x0e /* Mask for type bits */
/* Values found after masking with MACH_O_N_TYPE. */
#define MACH_O_N_UNDF 0x00 /* Undefined symbol */
#define MACH_O_N_ABS 0x02 /* Absolute symbol */
#define MACH_O_N_SECT 0x0e /* Defined in section from n_sect field */
/* Information we keep for a Mach-O symbol. */
@@ -316,8 +318,9 @@ static const char * const dwarf_section_names[DEBUG_MAX] =
/* Forward declaration. */
static int macho_add (struct backtrace_state *, const char *, int, off_t,
const unsigned char *, uintptr_t, int,
backtrace_error_callback, void *, fileline *, int *);
const unsigned char *, struct libbacktrace_base_address,
int, backtrace_error_callback, void *, fileline *,
int *);
/* A dummy callback function used when we can't find any debug info. */
@@ -495,10 +498,10 @@ macho_defined_symbol (uint8_t type)
{
if ((type & MACH_O_N_STAB) != 0)
return 0;
if ((type & MACH_O_N_EXT) != 0)
return 0;
switch (type & MACH_O_N_TYPE)
{
case MACH_O_N_UNDF:
return 0;
case MACH_O_N_ABS:
return 1;
case MACH_O_N_SECT:
@@ -512,7 +515,7 @@ macho_defined_symbol (uint8_t type)
static int
macho_add_symtab (struct backtrace_state *state, int descriptor,
uintptr_t base_address, int is_64,
struct libbacktrace_base_address base_address, int is_64,
off_t symoff, unsigned int nsyms, off_t stroff,
unsigned int strsize,
backtrace_error_callback error_callback, void *data)
@@ -627,7 +630,7 @@ macho_add_symtab (struct backtrace_state *state, int descriptor,
if (name[0] == '_')
++name;
macho_symbols[j].name = name;
macho_symbols[j].address = value + base_address;
macho_symbols[j].address = libbacktrace_add_base (value, base_address);
++j;
}
@@ -760,7 +763,8 @@ macho_syminfo (struct backtrace_state *state, uintptr_t addr,
static int
macho_add_fat (struct backtrace_state *state, const char *filename,
int descriptor, int swapped, off_t offset,
const unsigned char *match_uuid, uintptr_t base_address,
const unsigned char *match_uuid,
struct libbacktrace_base_address base_address,
int skip_symtab, uint32_t nfat_arch, int is_64,
backtrace_error_callback error_callback, void *data,
fileline *fileline_fn, int *found_sym)
@@ -862,7 +866,8 @@ macho_add_fat (struct backtrace_state *state, const char *filename,
static int
macho_add_dsym (struct backtrace_state *state, const char *filename,
uintptr_t base_address, const unsigned char *uuid,
struct libbacktrace_base_address base_address,
const unsigned char *uuid,
backtrace_error_callback error_callback, void *data,
fileline* fileline_fn)
{
@@ -980,7 +985,7 @@ macho_add_dsym (struct backtrace_state *state, const char *filename,
static int
macho_add (struct backtrace_state *state, const char *filename, int descriptor,
off_t offset, const unsigned char *match_uuid,
uintptr_t base_address, int skip_symtab,
struct libbacktrace_base_address base_address, int skip_symtab,
backtrace_error_callback error_callback, void *data,
fileline *fileline_fn, int *found_sym)
{
@@ -1242,7 +1247,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
c = _dyld_image_count ();
for (i = 0; i < c; ++i)
{
uintptr_t base_address;
struct libbacktrace_base_address base_address;
const char *name;
int d;
fileline mff;
@@ -1266,7 +1271,7 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
continue;
}
base_address = _dyld_get_image_vmaddr_slide (i);
base_address.m = _dyld_get_image_vmaddr_slide (i);
mff = macho_nodebug;
if (!macho_add (state, name, d, 0, NULL, base_address, 0,
@@ -1321,10 +1326,12 @@ backtrace_initialize (struct backtrace_state *state, const char *filename,
void *data, fileline *fileline_fn)
{
fileline macho_fileline_fn;
struct libbacktrace_base_address zero_base_address;
int found_sym;
macho_fileline_fn = macho_nodebug;
if (!macho_add (state, filename, descriptor, 0, NULL, 0, 0,
memset (&zero_base_address, 0, sizeof zero_base_address);
if (!macho_add (state, filename, descriptor, 0, NULL, zero_base_address, 0,
error_callback, data, &macho_fileline_fn, &found_sym))
return 0;

View File

@@ -47,7 +47,7 @@ public class tracy : ThirdPartyModule
switch (options.Platform.Target)
{
case TargetPlatform.Windows:
options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp");
options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=FlaxDbgHelp");
break;
case TargetPlatform.Switch:
options.PrivateDefinitions.Add("TRACY_USE_MALLOC");

View File

@@ -10,11 +10,13 @@
#endif
#ifndef TracyLine
# define TracyLine __LINE__
# define TracyLine TracyConcat(__LINE__,U) // MSVC Edit and continue __LINE__ is non-constant. See https://developercommunity.visualstudio.com/t/-line-cannot-be-used-as-an-argument-for-constexpr/195665
#endif
#ifndef TRACY_ENABLE
#define TracyNoop
#define ZoneNamed(x,y)
#define ZoneNamedN(x,y,z)
#define ZoneNamedC(x,y,z)
@@ -30,8 +32,12 @@
#define ZoneText(x,y)
#define ZoneTextV(x,y,z)
#define ZoneTextF(x,...)
#define ZoneTextVF(x,y,...)
#define ZoneName(x,y)
#define ZoneNameV(x,y,z)
#define ZoneNameF(x,...)
#define ZoneNameVF(x,y,...)
#define ZoneColor(x)
#define ZoneColorV(x,y)
#define ZoneValue(x)
@@ -53,8 +59,10 @@
#define TracyAlloc(x,y)
#define TracyFree(x)
#define TracyMemoryDiscard(x)
#define TracySecureAlloc(x,y)
#define TracySecureFree(x)
#define TracySecureMemoryDiscard(x)
#define TracyAllocN(x,y,z)
#define TracyFreeN(x,y)
@@ -76,8 +84,10 @@
#define TracyAllocS(x,y,z)
#define TracyFreeS(x,y)
#define TracyMemoryDiscardS(x,y)
#define TracySecureAllocS(x,y,z)
#define TracySecureFreeS(x,y)
#define TracySecureMemoryDiscardS(x,y)
#define TracyAllocNS(x,y,z,w)
#define TracyFreeNS(x,y,z)
@@ -93,9 +103,11 @@
#define TracyParameterRegister(x,y)
#define TracyParameterSetup(x,y,z,w)
#define TracyIsConnected false
#define TracyIsStarted false
#define TracySetProgramName(x)
#define TracyFiberEnter(x)
#define TracyFiberEnterHint(x,y)
#define TracyFiberLeave
#else
@@ -137,24 +149,21 @@ public:
};
}
#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK
# define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
# define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
# define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
# define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
# define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, TRACY_CALLSTACK, active )
# define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), TRACY_CALLSTACK, active )
#else
# define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active )
# define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active )
# define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active )
# define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), active )
# define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, active )
# define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), active )
#ifndef TRACY_CALLSTACK
#define TRACY_CALLSTACK 0
#endif
#define TracyNoop tracy::ProfilerAvailable()
#define ZoneNamed( varname, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
#define ZoneNamedN( varname, name, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
#define ZoneNamedC( varname, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
#define ZoneNamedNC( varname, name, color, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), TRACY_CALLSTACK, active )
#define ZoneTransient( varname, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, TRACY_CALLSTACK, active )
#define ZoneTransientN( varname, name, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), TRACY_CALLSTACK, active )
#define ZoneTransientNC( varname, name, color, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), color, TRACY_CALLSTACK, active )
#define ZoneScoped ZoneNamed( ___tracy_scoped_zone, true )
#define ZoneScopedN( name ) ZoneNamedN( ___tracy_scoped_zone, name, true )
#define ZoneScopedC( color ) ZoneNamedC( ___tracy_scoped_zone, color, true )
@@ -162,8 +171,12 @@ public:
#define ZoneText( txt, size ) ___tracy_scoped_zone.Text( txt, size )
#define ZoneTextV( varname, txt, size ) varname.Text( txt, size )
#define ZoneTextF( fmt, ... ) ___tracy_scoped_zone.TextFmt( fmt, ##__VA_ARGS__ )
#define ZoneTextVF( varname, fmt, ... ) varname.TextFmt( fmt, ##__VA_ARGS__ )
#define ZoneName( txt, size ) ___tracy_scoped_zone.Name( txt, size )
#define ZoneNameV( varname, txt, size ) varname.Name( txt, size )
#define ZoneNameF( fmt, ... ) ___tracy_scoped_zone.NameFmt( fmt, ##__VA_ARGS__ )
#define ZoneNameVF( varname, fmt, ... ) varname.NameFmt( fmt, ##__VA_ARGS__ )
#define ZoneColor( color ) ___tracy_scoped_zone.Color( color )
#define ZoneColorV( varname, color ) varname.Color( color )
#define ZoneValue( value ) ___tracy_scoped_zone.Value( value )
@@ -184,7 +197,7 @@ public:
#define TracySharedLockableN( type, varname, desc ) tracy::SharedLockable<type> varname { [] () -> const tracy::SourceLocationData* { static constexpr tracy::SourceLocationData srcloc { nullptr, desc, TracyFile, TracyLine, 0 }; return &srcloc; }() }
#define LockableBase( type ) tracy::Lockable<type>
#define SharedLockableBase( type ) tracy::SharedLockable<type>
#define LockMark( varname ) static constexpr tracy::SourceLocationData __tracy_lock_location_##varname { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; varname.Mark( &__tracy_lock_location_##varname )
#define LockMark( varname ) static constexpr tracy::SourceLocationData __tracy_lock_location_##__LINE__ { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; varname.Mark( &__tracy_lock_location_##__LINE__ )
#define LockableName( varname, txt, size ) varname.CustomName( txt, size )
#define TracyPlot( name, val ) tracy::Profiler::PlotData( name, val )
@@ -192,95 +205,52 @@ public:
#define TracyAppInfo( txt, size ) tracy::Profiler::MessageAppInfo( txt, size )
#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK
# define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, TRACY_CALLSTACK )
# define TracyMessageL( txt ) tracy::Profiler::Message( txt, TRACY_CALLSTACK )
# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, TRACY_CALLSTACK )
# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, TRACY_CALLSTACK )
#define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, TRACY_CALLSTACK )
#define TracyMessageL( txt ) tracy::Profiler::Message( txt, TRACY_CALLSTACK )
#define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, TRACY_CALLSTACK )
#define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, TRACY_CALLSTACK )
# define TracyAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, false )
# define TracyFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, false )
# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, true )
# define TracySecureFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, true )
#define TracyAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, false )
#define TracyFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, false )
#define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAllocCallstack( ptr, size, TRACY_CALLSTACK, true )
#define TracySecureFree( ptr ) tracy::Profiler::MemFreeCallstack( ptr, TRACY_CALLSTACK, true )
# define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, false, name )
# define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, false, name )
# define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, true, name )
# define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, true, name )
#else
# define TracyMessage( txt, size ) tracy::Profiler::Message( txt, size, 0 )
# define TracyMessageL( txt ) tracy::Profiler::Message( txt, 0 )
# define TracyMessageC( txt, size, color ) tracy::Profiler::MessageColor( txt, size, color, 0 )
# define TracyMessageLC( txt, color ) tracy::Profiler::MessageColor( txt, color, 0 )
#define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, false, name )
#define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, false, name )
#define TracyMemoryDiscard( name ) tracy::Profiler::MemDiscardCallstack( name, false, TRACY_CALLSTACK )
#define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, TRACY_CALLSTACK, true, name )
#define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, TRACY_CALLSTACK, true, name )
#define TracySecureMemoryDiscard( name ) tracy::Profiler::MemDiscardCallstack( name, true, TRACY_CALLSTACK )
# define TracyAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, false )
# define TracyFree( ptr ) tracy::Profiler::MemFree( ptr, false )
# define TracySecureAlloc( ptr, size ) tracy::Profiler::MemAlloc( ptr, size, true )
# define TracySecureFree( ptr ) tracy::Profiler::MemFree( ptr, true )
#define ZoneNamedS( varname, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
#define ZoneNamedNS( varname, name, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
#define ZoneNamedCS( varname, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
#define ZoneNamedNCS( varname, name, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
# define TracyAllocN( ptr, size, name ) tracy::Profiler::MemAllocNamed( ptr, size, false, name )
# define TracyFreeN( ptr, name ) tracy::Profiler::MemFreeNamed( ptr, false, name )
# define TracySecureAllocN( ptr, size, name ) tracy::Profiler::MemAllocNamed( ptr, size, true, name )
# define TracySecureFreeN( ptr, name ) tracy::Profiler::MemFreeNamed( ptr, true, name )
#endif
#define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active )
#define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active )
#ifdef TRACY_HAS_CALLSTACK
# define ZoneNamedS( varname, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
# define ZoneNamedNS( varname, name, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
# define ZoneNamedCS( varname, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { nullptr, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
# define ZoneNamedNCS( varname, name, color, depth, active ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; tracy::ScopedZone varname( &TracyConcat(__tracy_source_location,TracyLine), depth, active )
#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true )
#define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true )
#define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true )
#define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true )
# define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active )
# define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active )
#define TracyAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, false )
#define TracyFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, false )
#define TracySecureAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, true )
#define TracySecureFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, true )
# define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true )
# define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true )
# define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true )
# define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true )
#define TracyAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, false, name )
#define TracyFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, false, name )
#define TracyMemoryDiscardS( name, depth ) tracy::Profiler::MemDiscardCallstack( name, false, depth )
#define TracySecureAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, true, name )
#define TracySecureFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, true, name )
#define TracySecureMemoryDiscardS( name, depth ) tracy::Profiler::MemDiscardCallstack( name, true, depth )
# define TracyAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, false )
# define TracyFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, false )
# define TracySecureAllocS( ptr, size, depth ) tracy::Profiler::MemAllocCallstack( ptr, size, depth, true )
# define TracySecureFreeS( ptr, depth ) tracy::Profiler::MemFreeCallstack( ptr, depth, true )
# define TracyAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, false, name )
# define TracyFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, false, name )
# define TracySecureAllocNS( ptr, size, depth, name ) tracy::Profiler::MemAllocCallstackNamed( ptr, size, depth, true, name )
# define TracySecureFreeNS( ptr, depth, name ) tracy::Profiler::MemFreeCallstackNamed( ptr, depth, true, name )
# define TracyMessageS( txt, size, depth ) tracy::Profiler::Message( txt, size, depth )
# define TracyMessageLS( txt, depth ) tracy::Profiler::Message( txt, depth )
# define TracyMessageCS( txt, size, color, depth ) tracy::Profiler::MessageColor( txt, size, color, depth )
# define TracyMessageLCS( txt, color, depth ) tracy::Profiler::MessageColor( txt, color, depth )
#else
# define ZoneNamedS( varname, depth, active ) ZoneNamed( varname, active )
# define ZoneNamedNS( varname, name, depth, active ) ZoneNamedN( varname, name, active )
# define ZoneNamedCS( varname, color, depth, active ) ZoneNamedC( varname, color, active )
# define ZoneNamedNCS( varname, name, color, depth, active ) ZoneNamedNC( varname, name, color, active )
# define ZoneTransientS( varname, depth, active ) ZoneTransient( varname, active )
# define ZoneTransientNS( varname, name, depth, active ) ZoneTransientN( varname, name, active )
# define ZoneScopedS( depth ) ZoneScoped
# define ZoneScopedNS( name, depth ) ZoneScopedN( name )
# define ZoneScopedCS( color, depth ) ZoneScopedC( color )
# define ZoneScopedNCS( name, color, depth ) ZoneScopedNC( name, color )
# define TracyAllocS( ptr, size, depth ) TracyAlloc( ptr, size )
# define TracyFreeS( ptr, depth ) TracyFree( ptr )
# define TracySecureAllocS( ptr, size, depth ) TracySecureAlloc( ptr, size )
# define TracySecureFreeS( ptr, depth ) TracySecureFree( ptr )
# define TracyAllocNS( ptr, size, depth, name ) TracyAllocN( ptr, size, name )
# define TracyFreeNS( ptr, depth, name ) TracyFreeN( ptr, name )
# define TracySecureAllocNS( ptr, size, depth, name ) TracySecureAllocN( ptr, size, name )
# define TracySecureFreeNS( ptr, depth, name ) TracySecureFreeN( ptr, name )
# define TracyMessageS( txt, size, depth ) TracyMessage( txt, size )
# define TracyMessageLS( txt, depth ) TracyMessageL( txt )
# define TracyMessageCS( txt, size, color, depth ) TracyMessageC( txt, size, color )
# define TracyMessageLCS( txt, color, depth ) TracyMessageLC( txt, color )
#endif
#define TracyMessageS( txt, size, depth ) tracy::Profiler::Message( txt, size, depth )
#define TracyMessageLS( txt, depth ) tracy::Profiler::Message( txt, depth )
#define TracyMessageCS( txt, size, color, depth ) tracy::Profiler::MessageColor( txt, size, color, depth )
#define TracyMessageLCS( txt, color, depth ) tracy::Profiler::MessageColor( txt, color, depth )
#define TracySourceCallbackRegister( cb, data ) tracy::Profiler::SourceCallbackRegister( cb, data )
#define TracyParameterRegister( cb, data ) tracy::Profiler::ParameterRegister( cb, data )
@@ -289,7 +259,8 @@ public:
#define TracySetProgramName( name ) tracy::GetProfiler().SetProgramName( name );
#ifdef TRACY_FIBERS
# define TracyFiberEnter( fiber ) tracy::Profiler::EnterFiber( fiber )
# define TracyFiberEnter( fiber ) tracy::Profiler::EnterFiber( fiber, 0 )
# define TracyFiberEnterHint( fiber, groupHint ) tracy::Profiler::EnterFiber( fiber, groupHint )
# define TracyFiberLeave tracy::Profiler::LeaveFiber()
#endif