Imported Upstream version 4.8.0.472
Former-commit-id: 1fce58ff6397a8cf9d2fca6564b434613689c418
This commit is contained in:
parent
e5cd25ff4f
commit
5586c61ea6
20
man/mono.1
20
man/mono.1
@ -844,6 +844,26 @@ The offsets displayed are IL offsets.
|
||||
.PP
|
||||
A more powerful coverage tool is available in the module `monocov'.
|
||||
See the monocov(1) man page for details.
|
||||
.SH AOT PROFILING
|
||||
You can improve startup performance by using the AOT profiler.
|
||||
.PP
|
||||
Typically the AOT compiler (\fBmono --aot\fR) will not generate code
|
||||
for generic instantiations. To solve this, you can run Mono with the
|
||||
AOT profiler to find out all the generic instantiations that are used,
|
||||
and then instructing the AOT compiler to produce code for these.
|
||||
.PP
|
||||
This command will run the specified app.exe and produce the
|
||||
\fBout.aotprof\fR file with the data describing the generic
|
||||
instantiations that are needed:
|
||||
.nf
|
||||
$ mono --profile=aot:output=out.aotprof app.exe
|
||||
.fi
|
||||
.PP
|
||||
Once you have this data, you can pass this to Mono's AOT compiler to
|
||||
instruct it to generate code for it:
|
||||
.nf
|
||||
$ mono --aot=profile=out.aotprof
|
||||
.fi
|
||||
.SH DEBUGGING AIDS
|
||||
To debug managed applications, you can use the
|
||||
.B mdb
|
||||
|
@ -11,9 +11,9 @@
|
||||
namespace System.Transactions
|
||||
{
|
||||
public delegate Transaction HostCurrentTransactionCallback ();
|
||||
public delegate void TransactionCompletedEventHandler (object o,
|
||||
public delegate void TransactionCompletedEventHandler (object sender,
|
||||
TransactionEventArgs e);
|
||||
public delegate void TransactionStartedEventHandler (object o,
|
||||
public delegate void TransactionStartedEventHandler (object sender,
|
||||
TransactionEventArgs e);
|
||||
}
|
||||
|
||||
|
@ -189,6 +189,26 @@ namespace System.Transactions
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetDistributedTransactionIdentifier (IPromotableSinglePhaseNotification promotableNotification, Guid distributedTransactionIdentifier)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public bool EnlistPromotableSinglePhase (IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Guid promoterType)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public byte[] GetPromotedToken ()
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public Guid PromoterType
|
||||
{
|
||||
get { throw new NotImplementedException (); }
|
||||
}
|
||||
|
||||
[MonoTODO ("EnlistmentOptions being ignored")]
|
||||
public Enlistment EnlistVolatile (
|
||||
IEnlistmentNotification notification,
|
||||
@ -218,6 +238,17 @@ namespace System.Transactions
|
||||
return new Enlistment ();
|
||||
}
|
||||
|
||||
[MonoTODO ("Only Local Transaction Manager supported. Cannot have more than 1 durable resource per transaction.")]
|
||||
[PermissionSetAttribute (SecurityAction.LinkDemand)]
|
||||
public Enlistment PromoteAndEnlistDurable (
|
||||
Guid manager,
|
||||
IPromotableSinglePhaseNotification promotableNotification,
|
||||
ISinglePhaseNotification notification,
|
||||
EnlistmentOptions options)
|
||||
{
|
||||
throw new NotImplementedException ("DTC unsupported, multiple durable resource managers aren't supported.");
|
||||
}
|
||||
|
||||
public override bool Equals (object obj)
|
||||
{
|
||||
return Equals (obj as Transaction);
|
||||
|
@ -14,7 +14,7 @@ namespace System.Transactions
|
||||
[Serializable]
|
||||
public class TransactionException : SystemException
|
||||
{
|
||||
protected TransactionException ()
|
||||
public TransactionException ()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace System.Transactions
|
||||
[Serializable]
|
||||
public class TransactionInDoubtException : TransactionException
|
||||
{
|
||||
protected TransactionInDoubtException ()
|
||||
public TransactionInDoubtException ()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ namespace System.Transactions
|
||||
[MonoTODO]
|
||||
public static class TransactionInterop
|
||||
{
|
||||
public static readonly Guid PromoterTypeDtc = new Guid ("14229753-FFE1-428D-82B7-DF73045CB8DA");
|
||||
|
||||
[MonoTODO]
|
||||
public static IDtcTransaction GetDtcTransaction (Transaction transaction)
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace System.Transactions
|
||||
[Serializable]
|
||||
public class TransactionManagerCommunicationException : TransactionException
|
||||
{
|
||||
protected TransactionManagerCommunicationException ()
|
||||
public TransactionManagerCommunicationException ()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace System.Transactions
|
||||
[Serializable]
|
||||
public class TransactionPromotionException : TransactionException
|
||||
{
|
||||
protected TransactionPromotionException ()
|
||||
public TransactionPromotionException ()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,26 @@ namespace System.Transactions
|
||||
TransactionManager.DefaultTimeout, TransactionScopeAsyncFlowOption.Suppress);
|
||||
}
|
||||
|
||||
public TransactionScope (Transaction transactionToUse,
|
||||
TransactionScopeAsyncFlowOption asyncFlowOption)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public TransactionScope (Transaction transactionToUse,
|
||||
TimeSpan scopeTimeout,
|
||||
TransactionScopeAsyncFlowOption asyncFlowOption)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public TransactionScope (TransactionScopeOption scopeOption,
|
||||
TransactionOptions transactionOptions,
|
||||
TransactionScopeAsyncFlowOption asyncFlowOption)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
void Initialize (TransactionScopeOption scopeOption,
|
||||
Transaction tx, TransactionOptions options,
|
||||
DTCOption interop, TimeSpan timeout, TransactionScopeAsyncFlowOption asyncFlow)
|
||||
|
@ -20,6 +20,10 @@ $(error Unknown framework version)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PROFILE),build)
|
||||
CSC_RUNTIME_FLAGS=--profile=aot:output=$(topdir)/class/lib/build/csc.aotprofile
|
||||
endif
|
||||
|
||||
RESOURCE_STRINGS = ../referencesource/mscorlib/mscorlib.txt
|
||||
|
||||
LIBRARY_COMPILE = $(BOOT_COMPILE)
|
||||
|
@ -64,6 +64,15 @@ mono_btls_ssl_ctx_new (void)
|
||||
memset (ctx, 0, sizeof (MonoBtlsSslCtx));
|
||||
ctx->references = 1;
|
||||
ctx->ctx = SSL_CTX_new (TLS_method ());
|
||||
|
||||
// enable the default ciphers but disable any RC4 based ciphers
|
||||
// since they're insecure: RFC 7465 "Prohibiting RC4 Cipher Suites"
|
||||
SSL_CTX_set_cipher_list (ctx->ctx, "DEFAULT:!RC4");
|
||||
|
||||
// disable SSLv2 and SSLv3 by default, they are deprecated
|
||||
// and should generally not be used according to the openssl docs
|
||||
SSL_CTX_set_options (ctx->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,6 @@ mono_btls_ssl_new (MonoBtlsSslCtx *ctx)
|
||||
ptr->ctx = mono_btls_ssl_ctx_up_ref (ctx);
|
||||
ptr->ssl = SSL_new (mono_btls_ssl_ctx_get_ctx (ptr->ctx));
|
||||
|
||||
SSL_set_options (ptr->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
@ -1003,16 +1003,16 @@ mono_install_get_cached_class_info (MonoGetCachedClassInfo func);
|
||||
void
|
||||
mono_install_get_class_from_name (MonoGetClassFromName func);
|
||||
|
||||
MonoGenericContext*
|
||||
MONO_PROFILER_API MonoGenericContext*
|
||||
mono_class_get_context (MonoClass *klass);
|
||||
|
||||
MonoMethodSignature*
|
||||
MONO_PROFILER_API MonoMethodSignature*
|
||||
mono_method_signature_checked (MonoMethod *m, MonoError *err);
|
||||
|
||||
MonoGenericContext*
|
||||
mono_method_get_context_general (MonoMethod *method, gboolean uninflated);
|
||||
|
||||
MonoGenericContext*
|
||||
MONO_PROFILER_API MonoGenericContext*
|
||||
mono_method_get_context (MonoMethod *method);
|
||||
|
||||
/* Used by monodis, thus cannot be MONO_INTERNAL */
|
||||
@ -1296,7 +1296,7 @@ MONO_API void mono_class_describe_statics (MonoClass* klass);
|
||||
/* method debugging functions, for use inside gdb */
|
||||
MONO_API void mono_method_print_code (MonoMethod *method);
|
||||
|
||||
char *mono_signature_full_name (MonoMethodSignature *sig);
|
||||
MONO_PROFILER_API char *mono_signature_full_name (MonoMethodSignature *sig);
|
||||
|
||||
/*Enum validation related functions*/
|
||||
MONO_API gboolean
|
||||
@ -1305,6 +1305,12 @@ mono_type_is_valid_enum_basetype (MonoType * type);
|
||||
MONO_API gboolean
|
||||
mono_class_is_valid_enum (MonoClass *klass);
|
||||
|
||||
MONO_PROFILER_API gboolean
|
||||
mono_type_is_primitive (MonoType *type);
|
||||
|
||||
MONO_PROFILER_API gboolean
|
||||
mono_class_is_ginst (MonoClass *klass);
|
||||
|
||||
MonoType *
|
||||
mono_type_get_checked (MonoImage *image, guint32 type_token, MonoGenericContext *context, MonoError *error);
|
||||
|
||||
|
@ -1 +1 @@
|
||||
a1c77761cdbb42ae1e799cfd298fccf01c6bebeb
|
||||
ba097864604e44a8d83906ee85bdb3bb4091814b
|
@ -700,5 +700,7 @@ mono_context_init_checked (MonoDomain *domain, MonoError *error);
|
||||
gboolean
|
||||
mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error);
|
||||
|
||||
GPtrArray*
|
||||
mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly);
|
||||
|
||||
#endif /* __MONO_METADATA_DOMAIN_INTERNALS_H__ */
|
||||
|
@ -2052,3 +2052,24 @@ mono_domain_unlock (MonoDomain *domain)
|
||||
{
|
||||
mono_locks_coop_release (&domain->lock, DomainLock);
|
||||
}
|
||||
|
||||
GPtrArray*
|
||||
mono_domain_get_assemblies (MonoDomain *domain, gboolean refonly)
|
||||
{
|
||||
GSList *tmp;
|
||||
GPtrArray *assemblies;
|
||||
MonoAssembly *ass;
|
||||
|
||||
assemblies = g_ptr_array_new ();
|
||||
mono_domain_assemblies_lock (domain);
|
||||
for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
|
||||
ass = (MonoAssembly *)tmp->data;
|
||||
if (refonly != ass->ref_only)
|
||||
continue;
|
||||
if (ass->corlib_internal)
|
||||
continue;
|
||||
g_ptr_array_add (assemblies, ass);
|
||||
}
|
||||
mono_domain_assemblies_unlock (domain);
|
||||
return assemblies;
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
4eb9a6416a9d3d3b7e5830b49dd0629e4c81cc9a
|
||||
cc83f8242571580e8173442bdbb6d4ca671db338
|
@ -1032,6 +1032,103 @@ install_pe_loader (void)
|
||||
mono_install_image_loader (&pe_loader);
|
||||
}
|
||||
|
||||
/*
|
||||
Ignored assemblies.
|
||||
|
||||
There are some assemblies we need to ignore because they include an implementation that doesn't work under mono.
|
||||
Mono provides its own implementation of those assemblies so it's safe to do so.
|
||||
|
||||
The ignored_assemblies list is generated using tools/nuget-hash-extractor and feeding the problematic nugets to it.
|
||||
|
||||
Right now the list of nugets are the ones that provide the assemblies in $ignored_assemblies_names.
|
||||
|
||||
This is to be removed once a proper fix is shipped through nuget.
|
||||
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
SYS_RT_INTEROP_RUNTIME_INFO = 0, //System.Runtime.InteropServices.RuntimeInformation
|
||||
SYS_GLOBALIZATION_EXT = 1, //System.Globalization.Extensions
|
||||
SYS_IO_COMPRESSION = 2, //System.IO.Compression
|
||||
SYS_NET_HTTP = 3, //System.Net.Http
|
||||
SYS_TEXT_ENC_CODEPAGES = 4, //System.Text.Encoding.CodePages
|
||||
} IgnoredAssemblyNames;
|
||||
|
||||
typedef struct {
|
||||
int hash;
|
||||
int assembly_name;
|
||||
const char guid [40];
|
||||
} IgnoredAssembly;
|
||||
|
||||
const char *ignored_assemblies_names[] = {
|
||||
"System.Runtime.InteropServices.RuntimeInformation.dll",
|
||||
"System.Globalization.Extensions.dll",
|
||||
"System.IO.Compression.dll",
|
||||
"System.Net.Http.dll",
|
||||
"System.Text.Encoding.CodePages.dll"
|
||||
};
|
||||
|
||||
#define IGNORED_ASSEMBLY(HASH, NAME, GUID, VER_STR) { .hash = HASH, .assembly_name = NAME, .guid = GUID }
|
||||
|
||||
static const IgnoredAssembly ignored_assemblies [] = {
|
||||
IGNORED_ASSEMBLY (0x1136045D, SYS_GLOBALIZATION_EXT, "475DBF02-9F68-44F1-8FB5-C9F69F1BD2B1", "4.0.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x358C9723, SYS_GLOBALIZATION_EXT, "5FCD54F0-4B97-4259-875D-30E481F02EA2", "4.0.1 net46"),
|
||||
IGNORED_ASSEMBLY (0x450A096A, SYS_GLOBALIZATION_EXT, "E9FCFF5B-4DE1-4BDC-9CE8-08C640FC78CC", "4.3.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x7A39EA2D, SYS_IO_COMPRESSION, "C665DC9B-D9E5-4D00-98ED-E4F812F23545", "4.0.0 netcore50"),
|
||||
IGNORED_ASSEMBLY (0x1CBD59A2, SYS_IO_COMPRESSION, "44FCA06C-A510-4B3E-BDBF-D08D697EF65A", "4.1.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x5E393C29, SYS_IO_COMPRESSION, "3A58A219-266B-47C3-8BE8-4E4F394147AB", "4.3.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x726C7CC1, SYS_NET_HTTP, "7C0B577F-A4FD-47F1-ADF5-EE65B5A04BB5", "4.0.0 netcore50"),
|
||||
IGNORED_ASSEMBLY (0x27726A90, SYS_NET_HTTP, "269B562C-CC15-4736-B1B1-68D4A43CAA98", "4.1.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x10CADA75, SYS_NET_HTTP, "EA2EC6DC-51DD-479C-BFC2-E713FB9E7E47", "4.1.1 net46"),
|
||||
IGNORED_ASSEMBLY (0x8437178B, SYS_NET_HTTP, "C0E04D9C-70CF-48A6-A179-FBFD8CE69FD0", "4.3.0 net46"),
|
||||
IGNORED_ASSEMBLY (0x46A4A1C5, SYS_RT_INTEROP_RUNTIME_INFO, "F13660F8-9D0D-419F-BA4E-315693DD26EA", "4.0.0 net45"),
|
||||
IGNORED_ASSEMBLY (0xD07383BB, SYS_RT_INTEROP_RUNTIME_INFO, "DD91439F-3167-478E-BD2C-BF9C036A1395", "4.3.0 net45"),
|
||||
IGNORED_ASSEMBLY (0x911D9EC3, SYS_TEXT_ENC_CODEPAGES, "C142254F-DEB5-46A7-AE43-6F10320D1D1F", "4.0.1 net46"),
|
||||
IGNORED_ASSEMBLY (0xFA686A38, SYS_TEXT_ENC_CODEPAGES, "FD178CD4-EF4F-44D5-9C3F-812B1E25126B", "4.3.0 net46"),
|
||||
};
|
||||
|
||||
/*
|
||||
Equivalent C# code:
|
||||
static void Main () {
|
||||
string str = "...";
|
||||
int h = 5381;
|
||||
for (int i = 0; i < str.Length; ++i)
|
||||
h = ((h << 5) + h) ^ str[i];
|
||||
|
||||
Console.WriteLine ("{0:X}", h);
|
||||
}
|
||||
*/
|
||||
static int
|
||||
hash_guid (const char *str)
|
||||
{
|
||||
int h = 5381;
|
||||
while (*str) {
|
||||
h = ((h << 5) + h) ^ *str;
|
||||
++str;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_problematic_image (MonoImage *image)
|
||||
{
|
||||
int h = hash_guid (image->guid);
|
||||
|
||||
//TODO make this more cache effiecient.
|
||||
// Either sort by hash and bseach or use SoA and make the linear search more cache efficient.
|
||||
for (int i = 0; i < G_N_ELEMENTS (ignored_assemblies); ++i) {
|
||||
if (ignored_assemblies [i].hash == h && !strcmp (image->guid, ignored_assemblies [i].guid)) {
|
||||
const char *needle = ignored_assemblies_names [ignored_assemblies [i].assembly_name];
|
||||
size_t needle_len = strlen (needle);
|
||||
size_t asm_len = strlen (image->name);
|
||||
if (asm_len > needle_len && !g_ascii_strcasecmp (image->name + (asm_len - needle_len), needle))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static MonoImage *
|
||||
do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
|
||||
gboolean care_about_cli, gboolean care_about_pecoff)
|
||||
@ -1087,6 +1184,12 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
|
||||
if (!mono_image_load_cli_data (image))
|
||||
goto invalid_image;
|
||||
|
||||
if (!image->ref_only && is_problematic_image (image)) {
|
||||
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Denying load of problematic image %s", image->name);
|
||||
*status = MONO_IMAGE_IMAGE_INVALID;
|
||||
goto invalid_image;
|
||||
}
|
||||
|
||||
if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, &errors))
|
||||
goto invalid_image;
|
||||
|
||||
|
@ -861,7 +861,7 @@ EXTRA_DIST = TestDriver.cs \
|
||||
Makefile.am.in
|
||||
|
||||
version.h: Makefile
|
||||
echo "#define FULL_VERSION \"Stable 4.8.0.459/cd26828\"" > version.h
|
||||
echo "#define FULL_VERSION \"Stable 4.8.0.472/6f90ed1\"" > version.h
|
||||
|
||||
# Utility target for patching libtool to speed up linking
|
||||
patch-libtool:
|
||||
|
@ -861,7 +861,7 @@ EXTRA_DIST = TestDriver.cs \
|
||||
Makefile.am.in
|
||||
|
||||
version.h: Makefile
|
||||
echo "#define FULL_VERSION \"Stable 4.8.0.459/cd26828\"" > version.h
|
||||
echo "#define FULL_VERSION \"Stable 4.8.0.472/6f90ed1\"" > version.h
|
||||
|
||||
# Utility target for patching libtool to speed up linking
|
||||
patch-libtool:
|
||||
|
@ -1 +1 @@
|
||||
2009d30f4c7c81d73738eea5151254163a75de36
|
||||
433c6642ba1f57db104a712c141ab90d44e55c49
|
@ -1 +1 @@
|
||||
6dca377cb02fbc8588f0c3c97690bde945b3a67c
|
||||
c4f44c2ecbc5dc6f0532c59ad55c2f46f1ed7f73
|
@ -1 +1 @@
|
||||
941d5017d0fde6e040cd78412ee8cf2a08ca9376
|
||||
2fd57cf8561234d38c90801e15fa271c5a719a82
|
@ -15,6 +15,7 @@
|
||||
#include <mono/metadata/mono-debug.h>
|
||||
#include <mono/metadata/mono-debug-debugger.h>
|
||||
#include <mono/metadata/debug-mono-symfile.h>
|
||||
#include <mono/utils/mono-counters.h>
|
||||
|
||||
#if !defined(DISABLE_JIT) && !defined(DISABLE_LLDB)
|
||||
|
||||
@ -49,10 +50,15 @@ typedef struct
|
||||
guint32 version;
|
||||
/* Align */
|
||||
guint32 dummy;
|
||||
/* Keep these as pointers so accessing them is atomic */
|
||||
DebugEntry *entry;
|
||||
/* List of all entries */
|
||||
/* Keep this as a pointer so accessing it is atomic */
|
||||
DebugEntry *all_entries;
|
||||
/* The current entry embedded here to reduce the amount of roundtrips */
|
||||
guint32 type;
|
||||
guint32 dummy2;
|
||||
guint64 size;
|
||||
guint64 addr;
|
||||
} JitDescriptor;
|
||||
|
||||
/*
|
||||
@ -114,6 +120,8 @@ static GHashTable *codegen_regions;
|
||||
static DebugEntry *last_entry;
|
||||
static mono_mutex_t mutex;
|
||||
static GHashTable *dyn_codegen_regions;
|
||||
static double register_time;
|
||||
static int num_entries;
|
||||
|
||||
#define lldb_lock() mono_os_mutex_lock (&mutex)
|
||||
#define lldb_unlock() mono_os_mutex_unlock (&mutex)
|
||||
@ -291,7 +299,17 @@ add_entry (EntryType type, Buffer *buf)
|
||||
}
|
||||
|
||||
__mono_jit_debug_descriptor.entry = entry;
|
||||
|
||||
__mono_jit_debug_descriptor.type = entry->type;
|
||||
__mono_jit_debug_descriptor.size = entry->size;
|
||||
__mono_jit_debug_descriptor.addr = entry->addr;
|
||||
mono_memory_barrier ();
|
||||
|
||||
GTimer *timer = mono_time_track_start ();
|
||||
__mono_jit_debug_register_code ();
|
||||
mono_time_track_end (®ister_time, timer);
|
||||
num_entries ++;
|
||||
//printf ("%lf %d %d\n", register_time, num_entries, entry->type);
|
||||
|
||||
lldb_unlock ();
|
||||
}
|
||||
@ -395,6 +413,8 @@ mono_lldb_init (const char *options)
|
||||
{
|
||||
enabled = TRUE;
|
||||
mono_os_mutex_init_recursive (&mutex);
|
||||
|
||||
mono_counters_register ("Time spent in LLDB", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, ®ister_time);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
@ -600,6 +620,13 @@ mono_lldb_save_trampoline_info (MonoTrampInfo *info)
|
||||
void
|
||||
mono_lldb_save_specific_trampoline_info (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, gpointer code, guint32 code_len)
|
||||
{
|
||||
/*
|
||||
* Avoid emitting these for now,
|
||||
* they slow down execution too much, and they are
|
||||
* only needed during single stepping which doesn't
|
||||
* work anyway.
|
||||
*/
|
||||
#if 0
|
||||
TrampolineEntry *entry;
|
||||
UserData udata;
|
||||
int region_id;
|
||||
@ -635,6 +662,7 @@ mono_lldb_save_specific_trampoline_info (gpointer arg1, MonoTrampolineType tramp
|
||||
|
||||
add_entry (ENTRY_TRAMPOLINE, buf);
|
||||
buffer_free (buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1 +1 @@
|
||||
#define FULL_VERSION "Stable 4.8.0.459/cd26828"
|
||||
#define FULL_VERSION "Stable 4.8.0.472/6f90ed1"
|
||||
|
@ -111,6 +111,7 @@ testlog: $(PLOG_TESTS)
|
||||
check-local: $(check_targets)
|
||||
|
||||
EXTRA_DIST=mono-profiler-log.h \
|
||||
mono-profiler-aot.h \
|
||||
$(PLOG_TESTS_SRC) \
|
||||
ptestrunner.pl \
|
||||
$(suppression_DATA)
|
||||
|
@ -582,6 +582,7 @@ with_mono_path = MONO_PATH=$(CLASS)
|
||||
RUNTIME = $(with_mono_path) $(top_builddir)/runtime/mono-wrapper
|
||||
MCS = $(RUNTIME) $(mcs_topdir)/class/lib/build/mcs.exe -unsafe -nowarn:0162 -nowarn:0168 -nowarn:0219 -debug
|
||||
EXTRA_DIST = mono-profiler-log.h \
|
||||
mono-profiler-aot.h \
|
||||
$(PLOG_TESTS_SRC) \
|
||||
ptestrunner.pl \
|
||||
$(suppression_DATA)
|
||||
|
@ -13,11 +13,16 @@
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include "mono-profiler-aot.h"
|
||||
|
||||
#include <mono/metadata/profiler.h>
|
||||
#include <mono/metadata/tokentype.h>
|
||||
#include <mono/metadata/tabledefs.h>
|
||||
#include <mono/metadata/debug-helpers.h>
|
||||
#include <mono/metadata/assembly.h>
|
||||
#include <mono/metadata/class-internals.h>
|
||||
#include <mono/utils/mono-os-mutex.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
@ -29,90 +34,16 @@
|
||||
#endif
|
||||
|
||||
struct _MonoProfiler {
|
||||
GHashTable *classes;
|
||||
GHashTable *images;
|
||||
GPtrArray *methods;
|
||||
FILE *outfile;
|
||||
int id;
|
||||
char *outfile_name;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
GList *methods;
|
||||
} PerImageData;
|
||||
|
||||
typedef struct ForeachData {
|
||||
MonoProfiler *prof;
|
||||
FILE *outfile;
|
||||
MonoImage *image;
|
||||
MonoMethod *method;
|
||||
} ForeachData;
|
||||
|
||||
static void
|
||||
foreach_method (gpointer data, gpointer user_data)
|
||||
{
|
||||
ForeachData *udata = (ForeachData*)user_data;
|
||||
MonoMethod *method = (MonoMethod*)data;
|
||||
char *name;
|
||||
|
||||
if (!mono_method_get_token (method) || mono_class_get_image (mono_method_get_class (method)) != udata->image)
|
||||
return;
|
||||
|
||||
name = mono_method_full_name (method, TRUE);
|
||||
fprintf (udata->outfile, "%s\n", name);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
static void
|
||||
output_image (gpointer key, gpointer value, gpointer user_data)
|
||||
{
|
||||
MonoImage *image = (MonoImage*)key;
|
||||
PerImageData *image_data = (PerImageData*)value;
|
||||
MonoProfiler *prof = (MonoProfiler*)user_data;
|
||||
char *tmp, *outfile_name;
|
||||
FILE *outfile;
|
||||
int i, err;
|
||||
ForeachData data;
|
||||
|
||||
tmp = g_strdup_printf ("%s/.mono/aot-profile-data", g_get_home_dir ());
|
||||
|
||||
if (!g_file_test (tmp, G_FILE_TEST_IS_DIR)) {
|
||||
#ifdef HOST_WIN32
|
||||
err = mkdir (tmp);
|
||||
#else
|
||||
err = mkdir (tmp, 0777);
|
||||
#endif
|
||||
if (err) {
|
||||
fprintf (stderr, "mono-profiler-aot: Unable to create output directory '%s': %s\n", tmp, g_strerror (errno));
|
||||
exit (1);
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (TRUE) {
|
||||
outfile_name = g_strdup_printf ("%s/%s-%d", tmp, mono_image_get_name (image), i);
|
||||
|
||||
if (!g_file_test (outfile_name, G_FILE_TEST_IS_REGULAR))
|
||||
break;
|
||||
|
||||
i ++;
|
||||
}
|
||||
|
||||
printf ("Creating output file: %s\n", outfile_name);
|
||||
|
||||
outfile = fopen (outfile_name, "w+");
|
||||
g_assert (outfile);
|
||||
|
||||
fprintf (outfile, "#VER:%d\n", 2);
|
||||
|
||||
data.prof = prof;
|
||||
data.outfile = outfile;
|
||||
data.image = image;
|
||||
|
||||
g_list_foreach (image_data->methods, foreach_method, &data);
|
||||
}
|
||||
|
||||
/* called at the end of the program */
|
||||
static void
|
||||
prof_shutdown (MonoProfiler *prof)
|
||||
{
|
||||
g_hash_table_foreach (prof->images, output_image, prof);
|
||||
}
|
||||
static mono_mutex_t mutex;
|
||||
static gboolean verbose;
|
||||
|
||||
static void
|
||||
prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
|
||||
@ -123,15 +54,66 @@ static void
|
||||
prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
|
||||
{
|
||||
MonoImage *image = mono_class_get_image (mono_method_get_class (method));
|
||||
PerImageData *data;
|
||||
|
||||
data = (PerImageData *)g_hash_table_lookup (prof->images, image);
|
||||
if (!data) {
|
||||
data = g_new0 (PerImageData, 1);
|
||||
g_hash_table_insert (prof->images, image, data);
|
||||
if (!image->assembly || method->wrapper_type)
|
||||
return;
|
||||
|
||||
mono_os_mutex_lock (&mutex);
|
||||
g_ptr_array_add (prof->methods, method);
|
||||
mono_os_mutex_unlock (&mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
prof_shutdown (MonoProfiler *prof);
|
||||
|
||||
static void
|
||||
usage (int do_exit)
|
||||
{
|
||||
printf ("AOT profiler.\n");
|
||||
printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
|
||||
printf ("Options:\n");
|
||||
printf ("\thelp show this usage info\n");
|
||||
printf ("\toutput=FILENAME write the data to file FILENAME (required)\n");
|
||||
printf ("\tverbose print diagnostic info\n");
|
||||
if (do_exit)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
static const char*
|
||||
match_option (const char* p, const char *opt, char **rval)
|
||||
{
|
||||
int len = strlen (opt);
|
||||
if (strncmp (p, opt, len) == 0) {
|
||||
if (rval) {
|
||||
if (p [len] == '=' && p [len + 1]) {
|
||||
const char *opt = p + len + 1;
|
||||
const char *end = strchr (opt, ',');
|
||||
char *val;
|
||||
int l;
|
||||
if (end == NULL) {
|
||||
l = strlen (opt);
|
||||
} else {
|
||||
l = end - opt;
|
||||
}
|
||||
val = (char *) g_malloc (l + 1);
|
||||
memcpy (val, opt, l);
|
||||
val [l] = 0;
|
||||
*rval = val;
|
||||
return opt + l;
|
||||
}
|
||||
if (p [len] == 0 || p [len] == ',') {
|
||||
*rval = NULL;
|
||||
return p + len + (p [len] == ',');
|
||||
}
|
||||
usage (1);
|
||||
} else {
|
||||
if (p [len] == 0)
|
||||
return p + len;
|
||||
if (p [len] == ',')
|
||||
return p + len + 1;
|
||||
}
|
||||
}
|
||||
|
||||
data->methods = g_list_append (data->methods, method);
|
||||
return p;
|
||||
}
|
||||
|
||||
void
|
||||
@ -142,15 +124,282 @@ void
|
||||
mono_profiler_startup (const char *desc)
|
||||
{
|
||||
MonoProfiler *prof;
|
||||
const char *p;
|
||||
const char *opt;
|
||||
char *outfile_name;
|
||||
|
||||
p = desc;
|
||||
if (strncmp (p, "aot", 3))
|
||||
usage (1);
|
||||
p += 3;
|
||||
if (*p == ':')
|
||||
p++;
|
||||
for (; *p; p = opt) {
|
||||
char *val;
|
||||
if (*p == ',') {
|
||||
opt = p + 1;
|
||||
continue;
|
||||
}
|
||||
if ((opt = match_option (p, "help", NULL)) != p) {
|
||||
usage (0);
|
||||
continue;
|
||||
}
|
||||
if ((opt = match_option (p, "verbose", NULL)) != p) {
|
||||
verbose = TRUE;
|
||||
continue;
|
||||
}
|
||||
if ((opt = match_option (p, "output", &val)) != p) {
|
||||
outfile_name = val;
|
||||
continue;
|
||||
}
|
||||
fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!outfile_name) {
|
||||
fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
prof = g_new0 (MonoProfiler, 1);
|
||||
prof->images = g_hash_table_new (NULL, NULL);
|
||||
prof->classes = g_hash_table_new (NULL, NULL);
|
||||
prof->methods = g_ptr_array_new ();
|
||||
prof->outfile_name = outfile_name;
|
||||
|
||||
mono_os_mutex_init (&mutex);
|
||||
|
||||
mono_profiler_install (prof, prof_shutdown);
|
||||
|
||||
|
||||
mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
|
||||
|
||||
mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_byte (MonoProfiler *prof, guint8 value)
|
||||
{
|
||||
fwrite (&value, 1, 1, prof->outfile);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_int32 (MonoProfiler *prof, int value)
|
||||
{
|
||||
// FIXME: Endianness
|
||||
fwrite (&value, 4, 1, prof->outfile);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_string (MonoProfiler *prof, const char *str)
|
||||
{
|
||||
int len = strlen (str);
|
||||
|
||||
emit_int32 (prof, len);
|
||||
fwrite (str, len, 1, prof->outfile);
|
||||
}
|
||||
|
||||
static void
|
||||
emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
|
||||
{
|
||||
emit_byte (prof, type);
|
||||
emit_int32 (prof, id);
|
||||
}
|
||||
|
||||
static int
|
||||
add_image (MonoProfiler *prof, MonoImage *image)
|
||||
{
|
||||
int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
|
||||
if (id)
|
||||
return id - 1;
|
||||
|
||||
id = prof->id ++;
|
||||
emit_record (prof, AOTPROF_RECORD_IMAGE, id);
|
||||
emit_string (prof, image->assembly->aname.name);
|
||||
emit_string (prof, image->guid);
|
||||
g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
|
||||
return id;
|
||||
}
|
||||
|
||||
static int
|
||||
add_class (MonoProfiler *prof, MonoClass *klass);
|
||||
|
||||
static int
|
||||
add_type (MonoProfiler *prof, MonoType *type)
|
||||
{
|
||||
switch (type->type) {
|
||||
#if 0
|
||||
case MONO_TYPE_SZARRAY: {
|
||||
int eid = add_type (prof, &type->data.klass->byval_arg);
|
||||
if (eid == -1)
|
||||
return -1;
|
||||
int id = prof->id ++;
|
||||
emit_record (prof, AOTPROF_RECORD_TYPE, id);
|
||||
emit_byte (prof, MONO_TYPE_SZARRAY);
|
||||
emit_int32 (prof, id);
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
case MONO_TYPE_BOOLEAN:
|
||||
case MONO_TYPE_CHAR:
|
||||
case MONO_TYPE_I1:
|
||||
case MONO_TYPE_U1:
|
||||
case MONO_TYPE_I2:
|
||||
case MONO_TYPE_U2:
|
||||
case MONO_TYPE_I4:
|
||||
case MONO_TYPE_U4:
|
||||
case MONO_TYPE_I8:
|
||||
case MONO_TYPE_U8:
|
||||
case MONO_TYPE_R4:
|
||||
case MONO_TYPE_R8:
|
||||
case MONO_TYPE_I:
|
||||
case MONO_TYPE_U:
|
||||
case MONO_TYPE_OBJECT:
|
||||
case MONO_TYPE_STRING:
|
||||
case MONO_TYPE_CLASS:
|
||||
case MONO_TYPE_VALUETYPE:
|
||||
case MONO_TYPE_GENERICINST:
|
||||
return add_class (prof, mono_class_from_mono_type (type));
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
|
||||
{
|
||||
int i, id;
|
||||
int *ids;
|
||||
|
||||
// FIXME: Cache
|
||||
ids = g_malloc0 (inst->type_argc * sizeof (int));
|
||||
for (i = 0; i < inst->type_argc; ++i) {
|
||||
MonoType *t = inst->type_argv [i];
|
||||
ids [i] = add_type (prof, t);
|
||||
if (ids [i] == -1) {
|
||||
g_free (ids);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
id = prof->id ++;
|
||||
emit_record (prof, AOTPROF_RECORD_GINST, id);
|
||||
emit_int32 (prof, inst->type_argc);
|
||||
for (i = 0; i < inst->type_argc; ++i)
|
||||
emit_int32 (prof, ids [i]);
|
||||
g_free (ids);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static int
|
||||
add_class (MonoProfiler *prof, MonoClass *klass)
|
||||
{
|
||||
int id, inst_id = -1, image_id;
|
||||
char *name;
|
||||
|
||||
id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
|
||||
if (id)
|
||||
return id - 1;
|
||||
|
||||
image_id = add_image (prof, klass->image);
|
||||
|
||||
if (mono_class_is_ginst (klass)) {
|
||||
MonoGenericContext *ctx = mono_class_get_context (klass);
|
||||
inst_id = add_ginst (prof, ctx->class_inst);
|
||||
if (inst_id == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (klass->nested_in)
|
||||
name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
|
||||
else
|
||||
name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
|
||||
|
||||
id = prof->id ++;
|
||||
emit_record (prof, AOTPROF_RECORD_TYPE, id);
|
||||
emit_byte (prof, MONO_TYPE_CLASS);
|
||||
emit_int32 (prof, image_id);
|
||||
emit_int32 (prof, inst_id);
|
||||
emit_string (prof, name);
|
||||
g_free (name);
|
||||
g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
|
||||
return id;
|
||||
}
|
||||
|
||||
static void
|
||||
add_method (MonoProfiler *prof, MonoMethod *m)
|
||||
{
|
||||
MonoError error;
|
||||
MonoMethodSignature *sig;
|
||||
char *s;
|
||||
|
||||
sig = mono_method_signature_checked (m, &error);
|
||||
g_assert (mono_error_ok (&error));
|
||||
|
||||
int class_id = add_class (prof, m->klass);
|
||||
if (class_id == -1)
|
||||
return;
|
||||
int inst_id = -1;
|
||||
|
||||
if (m->is_inflated) {
|
||||
MonoGenericContext *ctx = mono_method_get_context (m);
|
||||
if (ctx->method_inst)
|
||||
inst_id = add_ginst (prof, ctx->method_inst);
|
||||
}
|
||||
int id = prof->id ++;
|
||||
emit_record (prof, AOTPROF_RECORD_METHOD, id);
|
||||
emit_int32 (prof, class_id);
|
||||
emit_int32 (prof, inst_id);
|
||||
emit_int32 (prof, sig->param_count);
|
||||
emit_string (prof, m->name);
|
||||
s = mono_signature_full_name (sig);
|
||||
emit_string (prof, s);
|
||||
g_free (s);
|
||||
if (verbose)
|
||||
printf ("%s %d\n", mono_method_full_name (m, 1), id);
|
||||
}
|
||||
|
||||
/* called at the end of the program */
|
||||
static void
|
||||
prof_shutdown (MonoProfiler *prof)
|
||||
{
|
||||
FILE *outfile;
|
||||
int mindex;
|
||||
char magic [32];
|
||||
|
||||
printf ("Creating output file: %s\n", prof->outfile_name);
|
||||
|
||||
outfile = fopen (prof->outfile_name, "w+");
|
||||
if (!outfile) {
|
||||
fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
|
||||
return;
|
||||
}
|
||||
prof->outfile = outfile;
|
||||
|
||||
gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
|
||||
sprintf (magic, AOT_PROFILER_MAGIC);
|
||||
fwrite (magic, strlen (magic), 1, outfile);
|
||||
emit_int32 (prof, version);
|
||||
|
||||
GHashTable *all_methods = g_hash_table_new (NULL, NULL);
|
||||
for (mindex = 0; mindex < prof->methods->len; ++mindex) {
|
||||
MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
|
||||
|
||||
if (!mono_method_get_token (m))
|
||||
continue;
|
||||
|
||||
if (g_hash_table_lookup (all_methods, m))
|
||||
continue;
|
||||
g_hash_table_insert (all_methods, m, m);
|
||||
|
||||
add_method (prof, m);
|
||||
}
|
||||
emit_record (prof, AOTPROF_RECORD_NONE, 0);
|
||||
|
||||
fclose (outfile);
|
||||
|
||||
g_hash_table_destroy (all_methods);
|
||||
g_hash_table_destroy (prof->classes);
|
||||
g_hash_table_destroy (prof->images);
|
||||
g_ptr_array_free (prof->methods, TRUE);
|
||||
g_free (prof->outfile_name);
|
||||
}
|
||||
|
33
mono/profiler/mono-profiler-aot.h
Normal file
33
mono/profiler/mono-profiler-aot.h
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __MONO_PROFILER_AOT_H__
|
||||
#define __MONO_PROFILER_AOT_H__
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/*
|
||||
* File format:
|
||||
* - magic
|
||||
* - major/minor version as an int, i.e. 0x00010001
|
||||
* - sequence of records terminated by a record with type TYPE_NONE
|
||||
* Record format:
|
||||
* - 1 byte record type (AotProfRecordType)
|
||||
* - 1 int record id
|
||||
* - record specific data
|
||||
* Encoding rules:
|
||||
* - int - 4 bytes little endian
|
||||
* - string - int length followed by data
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
AOTPROF_RECORD_NONE,
|
||||
AOTPROF_RECORD_IMAGE,
|
||||
AOTPROF_RECORD_TYPE,
|
||||
AOTPROF_RECORD_GINST,
|
||||
AOTPROF_RECORD_METHOD
|
||||
} AotProfRecordType;
|
||||
|
||||
#define AOT_PROFILER_MAGIC "AOTPROFILE"
|
||||
|
||||
#define AOT_PROFILER_MAJOR_VERSION 1
|
||||
#define AOT_PROFILER_MINOR_VERSION 0
|
||||
|
||||
#endif /* __MONO_PROFILER_AOT_H__ */
|
@ -275,6 +275,9 @@ typedef SSIZE_T ssize_t;
|
||||
#define MONO_LLVM_INTERNAL
|
||||
#endif
|
||||
|
||||
/* Used to mark internal functions used by the profiler modules */
|
||||
#define MONO_PROFILER_API MONO_API
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define MONO_ALWAYS_INLINE __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
|
BIN
po/mcs/de.gmo
BIN
po/mcs/de.gmo
Binary file not shown.
@ -1 +1 @@
|
||||
0b495da280aeb0edcc452a19ef2728a220e29836
|
||||
06d95eb77e22edab191d4d09927b233c5d2de3cb
|
BIN
po/mcs/es.gmo
BIN
po/mcs/es.gmo
Binary file not shown.
@ -1 +1 @@
|
||||
c1046188be652d3ba506a743ccd1edd5d769f03a
|
||||
c414cbd1a565f26f5628939e9a70676d3653d428
|
BIN
po/mcs/ja.gmo
BIN
po/mcs/ja.gmo
Binary file not shown.
@ -1 +1 @@
|
||||
15cfccc89d5d9835e017c1a4fe825b3685724c8d
|
||||
5030d72210cdffe84371eb3e9f1e89261daf28da
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: mono 4.8.0\n"
|
||||
"Report-Msgid-Bugs-To: http://www.mono-project.com/Bugs\n"
|
||||
"POT-Creation-Date: 2017-01-19 13:16+0000\n"
|
||||
"POT-Creation-Date: 2017-01-24 10:53+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
BIN
po/mcs/pt_BR.gmo
BIN
po/mcs/pt_BR.gmo
Binary file not shown.
@ -1 +1 @@
|
||||
3b041571f966e8ba5599018c48ef0a1ec7219b3e
|
||||
c9139a77cfe82ac0c042be33e9c22a6461d370c6
|
Loading…
x
Reference in New Issue
Block a user