mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-08 09:36:42 +02:00
FpDebug: Speed up fpdebug (GoNamedChild) by creating hashes for symbol names / Create Hash-lookup field for units to skip some CU
git-svn-id: trunk@63808 -
This commit is contained in:
parent
24a3af8855
commit
24d48052e3
@ -1256,7 +1256,9 @@ begin
|
|||||||
while i > 0 do begin
|
while i > 0 do begin
|
||||||
dec(i);
|
dec(i);
|
||||||
CU := FDwarf.CompilationUnits[i];
|
CU := FDwarf.CompilationUnits[i];
|
||||||
if CU = SkipCompUnit then
|
if (CU = SkipCompUnit) or
|
||||||
|
(not CU.KnownNameHashes^[ANameInfo.NameHash and KnownNameHashesBitMask])
|
||||||
|
then
|
||||||
continue;
|
continue;
|
||||||
//DebugLn(FPDBG_DWARF_SEARCH, ['TDbgDwarf.FindIdentifier search UNIT Name=', CU.FileName]);
|
//DebugLn(FPDBG_DWARF_SEARCH, ['TDbgDwarf.FindIdentifier search UNIT Name=', CU.FileName]);
|
||||||
|
|
||||||
@ -1275,7 +1277,6 @@ begin
|
|||||||
break;
|
break;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
CU.ScanAllEntries;
|
|
||||||
if InfoEntry.GoNamedChildEx(ANameInfo) then begin
|
if InfoEntry.GoNamedChildEx(ANameInfo) then begin
|
||||||
if InfoEntry.IsAddressInStartScope(FAddress) then begin
|
if InfoEntry.IsAddressInStartScope(FAddress) then begin
|
||||||
// only variables are marked "external", but types not / so we may need all top level
|
// only variables are marked "external", but types not / so we may need all top level
|
||||||
@ -1429,6 +1430,12 @@ begin
|
|||||||
//debugln(FPDBG_DWARF_SEARCH, ['TDbgDwarf.FindIdentifier Searching ', dbgs(InfoEntry.FScope, CU)]);
|
//debugln(FPDBG_DWARF_SEARCH, ['TDbgDwarf.FindIdentifier Searching ', dbgs(InfoEntry.FScope, CU)]);
|
||||||
StartScopeIdx := InfoEntry.ScopeIndex;
|
StartScopeIdx := InfoEntry.ScopeIndex;
|
||||||
|
|
||||||
|
tg := InfoEntry.AbbrevTag;
|
||||||
|
if (tg = DW_TAG_compile_unit) and
|
||||||
|
(not CU.KnownNameHashes^[NameInfo.NameHash and KnownNameHashesBitMask])
|
||||||
|
then
|
||||||
|
break;
|
||||||
|
|
||||||
//if InfoEntry.Abbrev = nil then
|
//if InfoEntry.Abbrev = nil then
|
||||||
// exit;
|
// exit;
|
||||||
|
|
||||||
@ -1462,7 +1469,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
tg := InfoEntry.AbbrevTag;
|
|
||||||
if (tg = DW_TAG_class_type) or (tg = DW_TAG_structure_type) then begin
|
if (tg = DW_TAG_class_type) or (tg = DW_TAG_structure_type) then begin
|
||||||
if FindSymbolInStructure(AName,NameInfo, InfoEntry, Result) then begin
|
if FindSymbolInStructure(AName,NameInfo, InfoEntry, Result) then begin
|
||||||
exit; // TODO: check error
|
exit; // TODO: check error
|
||||||
|
@ -113,6 +113,12 @@ type
|
|||||||
{$PACKRECORDS C}
|
{$PACKRECORDS C}
|
||||||
{%endregion Dwarf Header Structures }
|
{%endregion Dwarf Header Structures }
|
||||||
|
|
||||||
|
const
|
||||||
|
KnownNameHashesBitMask = $7FF; // 256 bytes
|
||||||
|
type
|
||||||
|
TKnownNameHashesArray = bitpacked array [0..KnownNameHashesBitMask] of Boolean;
|
||||||
|
PKnownNameHashesArray = ^TKnownNameHashesArray;
|
||||||
|
|
||||||
{%region Abbreviation Data / Section "debug_abbrev"}
|
{%region Abbreviation Data / Section "debug_abbrev"}
|
||||||
{ TDwarfAbbrev }
|
{ TDwarfAbbrev }
|
||||||
TDwarfAbbrevFlag = (
|
TDwarfAbbrevFlag = (
|
||||||
@ -200,6 +206,7 @@ type
|
|||||||
|
|
||||||
TNameSearchInfo = record
|
TNameSearchInfo = record
|
||||||
NameUpper, NameLower: String;
|
NameUpper, NameLower: String;
|
||||||
|
NameHash: Word;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{%region Information Entry / Section "debug_info"}
|
{%region Information Entry / Section "debug_info"}
|
||||||
@ -222,6 +229,7 @@ type
|
|||||||
TDwarfScopeInfoRec = record
|
TDwarfScopeInfoRec = record
|
||||||
Link: Integer;
|
Link: Integer;
|
||||||
Entry: Pointer;
|
Entry: Pointer;
|
||||||
|
NameHash: Word;
|
||||||
end;
|
end;
|
||||||
PDwarfScopeInfoRec = ^TDwarfScopeInfoRec;
|
PDwarfScopeInfoRec = ^TDwarfScopeInfoRec;
|
||||||
|
|
||||||
@ -240,6 +248,7 @@ type
|
|||||||
FIndex: Integer;
|
FIndex: Integer;
|
||||||
function GetChild: TDwarfScopeInfo; inline;
|
function GetChild: TDwarfScopeInfo; inline;
|
||||||
function GetChildIndex: Integer; inline;
|
function GetChildIndex: Integer; inline;
|
||||||
|
function GetCurrent: PDwarfScopeInfoRec;
|
||||||
function GetEntry: Pointer; inline;
|
function GetEntry: Pointer; inline;
|
||||||
function GetNext: TDwarfScopeInfo; inline;
|
function GetNext: TDwarfScopeInfo; inline;
|
||||||
function GetNextIndex: Integer; inline;
|
function GetNextIndex: Integer; inline;
|
||||||
@ -255,6 +264,7 @@ type
|
|||||||
function IsValid: Boolean; inline;
|
function IsValid: Boolean; inline;
|
||||||
property Index: Integer read FIndex write SetIndex;
|
property Index: Integer read FIndex write SetIndex;
|
||||||
property Entry: Pointer read GetEntry;
|
property Entry: Pointer read GetEntry;
|
||||||
|
property Current: PDwarfScopeInfoRec read GetCurrent;
|
||||||
|
|
||||||
function HasParent: Boolean; inline;
|
function HasParent: Boolean; inline;
|
||||||
function HasNext: Boolean; inline;
|
function HasNext: Boolean; inline;
|
||||||
@ -317,6 +327,8 @@ type
|
|||||||
function HasAttrib(AnAttrib: Cardinal): Boolean; inline;
|
function HasAttrib(AnAttrib: Cardinal): Boolean; inline;
|
||||||
property AttribForm[AnIdx: Integer]: Cardinal read GetAttribForm;
|
property AttribForm[AnIdx: Integer]: Cardinal read GetAttribForm;
|
||||||
|
|
||||||
|
procedure ComputeKnownHashes(AKNownHashes: PKnownNameHashesArray);
|
||||||
|
|
||||||
function GoNamedChild(AName: String): Boolean;
|
function GoNamedChild(AName: String): Boolean;
|
||||||
// find in enum too // TODO: control search with a flags param, if needed
|
// find in enum too // TODO: control search with a flags param, if needed
|
||||||
function GoNamedChildEx(const ANameInfo: TNameSearchInfo): Boolean;
|
function GoNamedChildEx(const ANameInfo: TNameSearchInfo): Boolean;
|
||||||
@ -581,9 +593,11 @@ type
|
|||||||
FScope: TDwarfScopeInfo;
|
FScope: TDwarfScopeInfo;
|
||||||
FScopeList: TDwarfScopeList;
|
FScopeList: TDwarfScopeList;
|
||||||
FScannedToEnd: Boolean;
|
FScannedToEnd: Boolean;
|
||||||
|
FKnownNameHashes: TKnownNameHashesArray;
|
||||||
|
|
||||||
procedure BuildAddressMap;
|
procedure BuildAddressMap;
|
||||||
function GetAddressMap: TMap;
|
function GetAddressMap: TMap;
|
||||||
|
function GetKnownNameHashes: PKnownNameHashesArray; inline;
|
||||||
function GetUnitName: String;
|
function GetUnitName: String;
|
||||||
function ReadTargetAddressFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
|
function ReadTargetAddressFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
|
||||||
function ReadDwarfSectionOffsetOrLenFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
|
function ReadDwarfSectionOffsetOrLenFromDwarfSection(var AData: Pointer; AIncPointer: Boolean = False): TFpDbgMemLocation;
|
||||||
@ -604,7 +618,6 @@ type
|
|||||||
function ReadValue(AAttribute: Pointer; AForm: Cardinal; out AValue: TByteDynArray; AnFormString: Boolean = False): Boolean;
|
function ReadValue(AAttribute: Pointer; AForm: Cardinal; out AValue: TByteDynArray; AnFormString: Boolean = False): Boolean;
|
||||||
// Read a value that contains an address. The address is evaluated using MapAddressToNewValue
|
// Read a value that contains an address. The address is evaluated using MapAddressToNewValue
|
||||||
function ReadAddressValue(AAttribute: Pointer; AForm: Cardinal; out AValue: QWord): Boolean;
|
function ReadAddressValue(AAttribute: Pointer; AForm: Cardinal; out AValue: QWord): Boolean;
|
||||||
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TFpDwarfInfo; ADebugFile: PDwarfDebugFile; ADataOffset: QWord; ALength: QWord; AVersion: Word; AAbbrevOffset: QWord; AAddressSize: Byte; AIsDwarf64: Boolean); virtual;
|
constructor Create(AOwner: TFpDwarfInfo; ADebugFile: PDwarfDebugFile; ADataOffset: QWord; ALength: QWord; AVersion: Word; AAbbrevOffset: QWord; AAddressSize: Byte; AIsDwarf64: Boolean); virtual;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -647,7 +660,7 @@ type
|
|||||||
property InfoDataLength: QWord read FLength; // length of info
|
property InfoDataLength: QWord read FLength; // length of info
|
||||||
property AddressMap: TMap read GetAddressMap;
|
property AddressMap: TMap read GetAddressMap;
|
||||||
property AbbrevList: TDwarfAbbrevList read FAbbrevList;
|
property AbbrevList: TDwarfAbbrevList read FAbbrevList;
|
||||||
|
property KnownNameHashes: PKnownNameHashesArray read GetKnownNameHashes; // Only for TOP-LEVEL entries
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TFpDwarfInfo }
|
{ TFpDwarfInfo }
|
||||||
@ -772,6 +785,7 @@ function NameInfoForSearch(const AName: String): TNameSearchInfo;
|
|||||||
begin
|
begin
|
||||||
Result.NameLower := UTF8LowerCase(AName);
|
Result.NameLower := UTF8LowerCase(AName);
|
||||||
Result.NameUpper := UTF8UpperCase(AName);
|
Result.NameUpper := UTF8UpperCase(AName);
|
||||||
|
Result.NameHash := objpas.Hash(Result.NameUpper) and $7fff or $8000;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Dbgs(AInfoData: Pointer; ACompUnit: TDwarfCompilationUnit): String;
|
function Dbgs(AInfoData: Pointer; ACompUnit: TDwarfCompilationUnit): String;
|
||||||
@ -1632,6 +1646,13 @@ begin
|
|||||||
Result := -1;
|
Result := -1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDwarfScopeInfo.GetCurrent: PDwarfScopeInfoRec;
|
||||||
|
begin
|
||||||
|
Result := nil;
|
||||||
|
if IsValid then
|
||||||
|
Result := @FScopeList^.List[FIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
function TDwarfScopeInfo.GetParent: TDwarfScopeInfo;
|
function TDwarfScopeInfo.GetParent: TDwarfScopeInfo;
|
||||||
var
|
var
|
||||||
l: Integer;
|
l: Integer;
|
||||||
@ -2520,6 +2541,55 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TDwarfInformationEntry.ComputeKnownHashes(
|
||||||
|
AKNownHashes: PKnownNameHashesArray);
|
||||||
|
var
|
||||||
|
EntryName: PChar;
|
||||||
|
NextTopLevel: Integer;
|
||||||
|
h: LongWord;
|
||||||
|
InEnum: Boolean;
|
||||||
|
begin
|
||||||
|
InEnum := False;
|
||||||
|
if not HasValidScope then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
NextTopLevel := 0;
|
||||||
|
dec(FScope.FIndex);
|
||||||
|
while (FScope.Index < FScope.FScopeList^.HighestKnown) do begin
|
||||||
|
inc(FScope.FIndex);
|
||||||
|
ScopeChanged;
|
||||||
|
PrepareAbbrev;
|
||||||
|
|
||||||
|
if not (dafHasName in FAbbrev^.flags) then begin
|
||||||
|
if FScope.Index >= NextTopLevel then
|
||||||
|
NextTopLevel := FScope.GetNextIndex;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if not ReadValue(DW_AT_name, EntryName) then begin
|
||||||
|
if FScope.Index >= NextTopLevel then
|
||||||
|
NextTopLevel := FScope.GetNextIndex;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
h := objpas.Hash(UTF8UpperCase(EntryName)) and $7fff or $8000;
|
||||||
|
FScope.Current^.NameHash := h;
|
||||||
|
if (FScope.Index >= NextTopLevel) or InEnum then
|
||||||
|
AKNownHashes^[h and KnownNameHashesBitMask] := True;
|
||||||
|
|
||||||
|
if FScope.Index >= NextTopLevel then begin
|
||||||
|
InEnum := False;
|
||||||
|
if FAbbrev^.tag = DW_TAG_enumeration_type then begin
|
||||||
|
InEnum := True;
|
||||||
|
NextTopLevel := FScope.GetNextIndex;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FScope.Index >= NextTopLevel then
|
||||||
|
NextTopLevel := FScope.GetNextIndex;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TDwarfInformationEntry.GetScopeIndex: Integer;
|
function TDwarfInformationEntry.GetScopeIndex: Integer;
|
||||||
begin
|
begin
|
||||||
Result := FScope.Index;
|
Result := FScope.Index;
|
||||||
@ -2573,6 +2643,7 @@ var
|
|||||||
EntryName: PChar;
|
EntryName: PChar;
|
||||||
InEnum: Boolean;
|
InEnum: Boolean;
|
||||||
ParentScopIdx: Integer;
|
ParentScopIdx: Integer;
|
||||||
|
sc: PDwarfScopeInfoRec;
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
InEnum := False;
|
InEnum := False;
|
||||||
@ -2583,22 +2654,31 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
while true do begin
|
while true do begin
|
||||||
while HasValidScope do begin
|
while HasValidScope do begin
|
||||||
|
sc := FScope.Current;
|
||||||
|
if sc^.NameHash = 0 then begin
|
||||||
|
GoNext;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
|
||||||
PrepareAbbrev;
|
PrepareAbbrev;
|
||||||
if not (dafHasName in FAbbrev^.flags) then begin
|
if not (dafHasName in FAbbrev^.flags) then begin
|
||||||
|
assert(false);
|
||||||
GoNext;
|
GoNext;
|
||||||
Continue;
|
Continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if not ReadValue(DW_AT_name, EntryName) then begin
|
if (sc^.NameHash = ANameInfo.NameHash) then begin
|
||||||
GoNext;
|
if not ReadValue(DW_AT_name, EntryName) then begin
|
||||||
Continue;
|
GoNext;
|
||||||
end;
|
Continue;
|
||||||
|
end;
|
||||||
|
|
||||||
if CompareUtf8BothCase(PChar(ANameInfo.NameUpper), PChar(ANameInfo.nameLower), EntryName) then begin
|
if CompareUtf8BothCase(PChar(ANameInfo.NameUpper), PChar(ANameInfo.nameLower), EntryName) then begin
|
||||||
// TODO: check DW_AT_start_scope;
|
// TODO: check DW_AT_start_scope;
|
||||||
DebugLn(FPDBG_DWARF_SEARCH, ['GoNamedChildEX found ', dbgs(FScope, FCompUnit), ' Result=', DbgSName(Self), ' FOR ', ANameInfo.nameLower]);
|
DebugLn(FPDBG_DWARF_SEARCH, ['GoNamedChildEX found ', dbgs(FScope, FCompUnit), ' Result=', DbgSName(Self), ' FOR ', ANameInfo.nameLower]);
|
||||||
Result := True;
|
Result := True;
|
||||||
exit;
|
exit;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if FAbbrev^.tag = DW_TAG_enumeration_type then begin
|
if FAbbrev^.tag = DW_TAG_enumeration_type then begin
|
||||||
@ -2626,6 +2706,7 @@ function TDwarfInformationEntry.GoNamedChildMatchCaseEx(
|
|||||||
const ANameInfo: TNameSearchInfo): Boolean;
|
const ANameInfo: TNameSearchInfo): Boolean;
|
||||||
var
|
var
|
||||||
EntryName: PChar;
|
EntryName: PChar;
|
||||||
|
sc: PDwarfScopeInfoRec;
|
||||||
begin
|
begin
|
||||||
Result := False;
|
Result := False;
|
||||||
if ANameInfo.NameUpper = '' then
|
if ANameInfo.NameUpper = '' then
|
||||||
@ -2635,25 +2716,34 @@ begin
|
|||||||
exit;
|
exit;
|
||||||
|
|
||||||
while HasValidScope do begin
|
while HasValidScope do begin
|
||||||
|
sc := FScope.Current;
|
||||||
|
if sc^.NameHash = 0 then begin
|
||||||
|
GoNext;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
|
||||||
PrepareAbbrev;
|
PrepareAbbrev;
|
||||||
if not (dafHasName in FAbbrev^.flags) then begin
|
if not (dafHasName in FAbbrev^.flags) then begin
|
||||||
|
Assert(false);
|
||||||
GoNext;
|
GoNext;
|
||||||
Continue;
|
Continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if not ReadValue(DW_AT_name, EntryName) then begin
|
if (sc^.NameHash = ANameInfo.NameHash) then begin
|
||||||
GoNext;
|
if not ReadValue(DW_AT_name, EntryName) then begin
|
||||||
Continue;
|
GoNext;
|
||||||
|
Continue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if CompareMem(PChar(ANameInfo.nameLower), @EntryName, Length(EntryName)) then begin
|
||||||
|
// TODO: check DW_AT_start_scope;
|
||||||
|
DebugLn(FPDBG_DWARF_SEARCH, ['GoNamedChildEX found ', dbgs(FScope, FCompUnit), ' Result=', DbgSName(Self), ' FOR ', ANameInfo.nameLower]);
|
||||||
|
Result := True;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if CompareMem(PChar(ANameInfo.nameLower), @EntryName, Length(EntryName)) then begin
|
GoNext;
|
||||||
// TODO: check DW_AT_start_scope;
|
|
||||||
DebugLn(FPDBG_DWARF_SEARCH, ['GoNamedChildEX found ', dbgs(FScope, FCompUnit), ' Result=', DbgSName(Self), ' FOR ', ANameInfo.nameLower]);
|
|
||||||
Result := True;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
GoNext;
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -3827,6 +3917,11 @@ begin
|
|||||||
Result := FAddressMap;
|
Result := FAddressMap;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TDwarfCompilationUnit.GetKnownNameHashes: PKnownNameHashesArray;
|
||||||
|
begin
|
||||||
|
Result := @FKnownNameHashes;
|
||||||
|
end;
|
||||||
|
|
||||||
function TDwarfCompilationUnit.FullFileName(const AFileName: string): String;
|
function TDwarfCompilationUnit.FullFileName(const AFileName: string): String;
|
||||||
begin
|
begin
|
||||||
Result := AFileName;
|
Result := AFileName;
|
||||||
@ -3870,8 +3965,6 @@ var
|
|||||||
begin
|
begin
|
||||||
if FAddressMapBuild then Exit;
|
if FAddressMapBuild then Exit;
|
||||||
|
|
||||||
ScanAllEntries;
|
|
||||||
|
|
||||||
Scope := FScope;
|
Scope := FScope;
|
||||||
ScopeIdx := Scope.Index;
|
ScopeIdx := Scope.Index;
|
||||||
|
|
||||||
@ -4015,6 +4108,7 @@ var
|
|||||||
Form: Cardinal;
|
Form: Cardinal;
|
||||||
StatementListOffs, Offs: QWord;
|
StatementListOffs, Offs: QWord;
|
||||||
Scope: TDwarfScopeInfo;
|
Scope: TDwarfScopeInfo;
|
||||||
|
InfoEntry: TDwarfInformationEntry;
|
||||||
begin
|
begin
|
||||||
//DebugLn(FPDBG_DWARF_VERBOSE, ['-- compilation unit --']);
|
//DebugLn(FPDBG_DWARF_VERBOSE, ['-- compilation unit --']);
|
||||||
//DebugLn(FPDBG_DWARF_VERBOSE, [' data offset: ', ADataOffset]);
|
//DebugLn(FPDBG_DWARF_VERBOSE, [' data offset: ', ADataOffset]);
|
||||||
@ -4069,6 +4163,12 @@ begin
|
|||||||
end;
|
end;
|
||||||
FValid := True;
|
FValid := True;
|
||||||
|
|
||||||
|
ScanAllEntries;
|
||||||
|
InfoEntry := TDwarfInformationEntry.Create(Self, nil);
|
||||||
|
InfoEntry.ScopeIndex := FirstScope.Index;
|
||||||
|
InfoEntry.ComputeKnownHashes(@FKnownNameHashes);
|
||||||
|
InfoEntry.ReleaseReference;
|
||||||
|
|
||||||
AttribList.EvalCount := 0;
|
AttribList.EvalCount := 0;
|
||||||
/// TODO: (dafHasName in Abbrev.flags)
|
/// TODO: (dafHasName in Abbrev.flags)
|
||||||
if LocateAttribute(Scope.Entry, DW_AT_name, AttribList, Attrib, Form)
|
if LocateAttribute(Scope.Entry, DW_AT_name, AttribList, Attrib, Form)
|
||||||
|
Loading…
Reference in New Issue
Block a user