[PATCH] add clear command
by Huang Shijie
Add the clear command for crash.
Use it to clear the screen.
Signed-off-by: Huang Shijie <shijie(a)os.amperecomputing.com>
---
This is just for tool "crash".
---
defs.h | 2 ++
global_data.c | 1 +
help.c | 8 ++++++++
tools.c | 9 +++++++++
4 files changed, 20 insertions(+)
diff --git a/defs.h b/defs.h
index 96a7a2a..a72aa69 100644
--- a/defs.h
+++ b/defs.h
@@ -5283,6 +5283,7 @@ void cmd_test(void); /* test.c */
void cmd_ascii(void); /* tools.c */
void cmd_sbitmapq(void); /* sbitmap.c */
void cmd_bpf(void); /* bfp.c */
+void cmd_clear(void); /* tools.c */
void cmd_set(void); /* tools.c */
void cmd_eval(void); /* tools.c */
void cmd_list(void); /* tools.c */
@@ -5883,6 +5884,7 @@ extern char *help_ascii[];
extern char *help_bpf[];
extern char *help_bt[];
extern char *help_btop[];
+extern char *help_clear[];
extern char *help_dev[];
extern char *help_dis[];
extern char *help_eval[];
diff --git a/global_data.c b/global_data.c
index f9bb7d0..2157e8c 100644
--- a/global_data.c
+++ b/global_data.c
@@ -75,6 +75,7 @@ struct command_table_entry linux_command_table[] = {
{"bpf", cmd_bpf, help_bpf, 0},
{"bt", cmd_bt, help_bt, REFRESH_TASK_TABLE},
{"btop", cmd_btop, help_btop, 0},
+ {"clear", cmd_clear, help_clear, 0},
{"dev", cmd_dev, help_dev, 0},
{"dis", cmd_dis, help_dis, MINIMAL},
{"eval", cmd_eval, help_eval, MINIMAL},
diff --git a/help.c b/help.c
index cc7ab20..04284e5 100644
--- a/help.c
+++ b/help.c
@@ -2293,6 +2293,14 @@ char *help_btop[] = {
NULL
};
+char *help_clear[] = {
+"clear",
+"Clear the screen",
+" ",
+"Used to clear the screen",
+NULL
+};
+
char *help_extend[] = {
"extend",
"extend the %s command set",
diff --git a/tools.c b/tools.c
index 0f2db10..96c8dbe 100644
--- a/tools.c
+++ b/tools.c
@@ -1783,6 +1783,15 @@ mkstring(char *s, int size, ulong flags, const char *opt)
return(s);
}
+/*
+ * Clear the screen.
+ */
+void
+cmd_clear(void)
+{
+ system("clear");
+}
+
/*
* Prints the requested number of BACKSPACE characters.
*/
--
2.39.2
1 year, 1 month
Re: [Crash-utility] [PATCH] diskdump: fix read_diskdump() returns successfully for illegal 0-size page descriptors
by lijiang
Thank you for the patch, HATAYAMA.
On Thu, Sep 7, 2023 at 6:55 PM <crash-utility-request(a)redhat.com> wrote:
> Date: Thu, 7 Sep 2023 18:38:25 +0900
> From: HATAYAMA Daisuke <d.hatayama(a)fujitsu.com>
> To: crash-utility(a)redhat.com
> Subject: [Crash-utility] [PATCH] diskdump: fix read_diskdump() returns
> successfully for illegal 0-size page descriptors
> Message-ID: <20230907093825.515-1-d.hatayama(a)fujitsu.com>
> Content-Type: text/plain; charset="US-ASCII"; x-default=true
>
> read_diskdump() returns successfully for illegal 0-size page
> descriptors. Page descriptors are illegal if their size member holds 0
> because makedumpfile never puts 0 there because any data never result
> in 0 byte by compression. If page descriptors hold 0 in size member,
> it means the crash dump file is corrupted for some reason.
>
> The root cause of this is that sanity check of function cache_page()
> doesn't focus on such 0-size page descriptors. Then, the 0-size page
> descriptor is passed to pread(), pread() immediately returns 0
> successfully because read data is 0 byte, and then read_diskdump()
> returns successfully.
>
> To fix this issue, let the sanity check take into account such 0-size
> page descriptors and read_diskdump() result in READ_ERROR.
>
> Signed-off-by: HATAYAMA Daisuke <d.hatayama(a)fujitsu.com>
> ---
> diskdump.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/diskdump.c b/diskdump.c
> index 2c284ff..2be7cc7 100644
> --- a/diskdump.c
> +++ b/diskdump.c
> @@ -1210,7 +1210,7 @@ cache_page(physaddr_t paddr)
> return ret;
>
> /* sanity check */
> - if (pd.size > block_size)
> + if (pd.size > block_size || !pd.size)
>
Actually this may not be a real read error instead of an incomplete page
due to corruption or other reasons. Given that, I would suggest adding the
sanity check as below:
- } else if (0 == pd.offset) {
+ } else if (0 == pd.offset || !pd.size) {
It can help to print more information according to the code when fails on
the page:
/*
* First check whether zero_excluded has been set.
*/
if (*diskdump_flags & ZERO_EXCLUDED) {
if (CRASHDEBUG(8))
fprintf(fp,
"read_diskdump/cache_page: zero-fill: "
"paddr/pfn: %llx/%lx\n",
(ulonglong)paddr, pfn);
memset(dd->compressed_page, 0, dd->block_size);
} else {
if (CRASHDEBUG(8))
fprintf(fp,
"read_diskdump/cache_page: "
"descriptor with zero offset found
at "
"paddr/pfn/pos: %llx/%lx/%lx\n",
(ulonglong)paddr, pfn, desc_pos);
return PAGE_INCOMPLETE;
}
I don't have such vmcores, and can not test it, just an idea.
Thanks.
Lianbo
return READ_ERROR;
>
> /* read page data */
> --
> 2.25.1
>
1 year, 1 month
About referring to struct load_module from extension modules
by Daisuke Hatayama (Fujitsu)
Hagio-san,
crash trace now results in SIGSEGV on Fedora Linux 38 as follows:
crash> extend /usr/lib64/crash/extensions/trace.so
/usr/lib64/crash/extensions/trace.so: shared object loaded
crash> trace
current tracer is nop
crash> trace show
Segmentation fault (core dumped)
The SIGSEGV occurs in function save_proc_kallsyms() when trying to
look up module symbols via lm->mod_symtable:
https://github.com/fujitsu/crash-trace/blob/master/trace.c#L2222-L2246
crash trace command creates a trace data that contains kallsyms
internally. crash trace command refer to lm->mod_symtable to create
the kallsyms. See
https://man7.org/linux/man-pages/man5/trace-cmd.dat.5.html#KALLSYMS_INFOR...
in details.
This issue occurs when using crash utility with the following commit that is for
new kernel module layout support on Linux kernel 6.4:
commit 7750e61fdb2a083f26156a5338aa2ebe26447f3f
Author: Kazuhito Hagio <k-hagio-ab(a)nec.com>
Date: Thu Jun 22 16:09:07 2023 +0900
Support module memory layout change on Linux 6.4
Support module memory layout change on Linux 6.4 by kernel commit
ac3b43283923 ("module: replace module_layout with module_memory") [1].
Without the patch, crash cannot even start a session with an error
message like this:
crash: invalid structure member offset: module_core_size
FILE: kernel.c LINE: 3787 FUNCTION: module_init()
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit...
Signed-off-by: Kazuhito Hagio <k-hagio-ab(a)nec.com>
I've already made a tentative fixing patch based on this new handling
of struct load_module based on function module_symbol_dump() and it
works fine.
On the other hand, is it OK to refer to struct load_module directly
from extension modules?
I'm not the original author of crash trace command. I've just found
crash trace doing like this for the first time.
- If it is OK to refer to struct load_module directly from extension
modules, should it be backward compatible on it? Or extension
modules should absorb the incompatibility?
- If it is not OK, is there possibility to introduce a new interface
to iterate symbols without need of referring to struct load_modules
directly?
Thanks.
HATAYAMA, Daisuke
1 year, 1 month
[PATCH] Fix "vtop" command to display the swapinfo for arm64 kernel 5.19.0+
by Guanyou Chen
arm64/pgtable: support __HAVE_ARCH_PTE_SWP_EXCLUSIVE
Let's use one of the type bits: core-mm only supports 5, so there is no
need to consume 6.
Note that we might be able to reuse bit 1, but reusing bit 1 turned out
problematic in the past for PROT_NONE handling; so let's play safe and use
another bit.
Link: https://lkml.kernel.org/r/20220329164329.208407-5-david@redhat.com
Before:
crash> vtop 70504000
VIRTUAL PHYSICAL
70504000 (not mapped)
PAGE DIRECTORY: ffffff80f265c000
PGD: ffffff80f265c008 => 800000141537003
PMD: ffffff8101537c10 => 800000141538003
PTE: ffffff8101538820 => 12bc3e04
PTE vtop: cannot determine swap location
After:
crash> vtop 70504000
VIRTUAL PHYSICAL
70504000 (not mapped)
PAGE DIRECTORY: ffffff80f265c000
PGD: ffffff80f265c008 => 800000141537003
PMD: ffffff8101537c10 => 800000141538003
PTE: ffffff8101538820 => 12bc3e04
PTE SWAP OFFSET
12bc3e04 /first_stage_ramdisk/dev/block/zram0 1227838
VMA START END FLAGS FILE
ffffff80dfe7b578 70504000 707bd000 100073
SWAP: /first_stage_ramdisk/dev/block/zram0 OFFSET: 1227838
Signed-off-by: chenguanyou <chenguanyou(a)xiaomi.com>
---
arm64.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/arm64.c b/arm64.c
index 39d5f04..ea1b51b 100644
--- a/arm64.c
+++ b/arm64.c
@@ -468,8 +468,16 @@ arm64_init(int when)
}
}
-
- if (THIS_KERNEL_VERSION >= LINUX(4,0,0)) {
+ if (THIS_KERNEL_VERSION >= LINUX(5,19,0)) {
+ ms->__SWP_TYPE_BITS = 5;
+ ms->__SWP_TYPE_SHIFT = 3;
+ ms->__SWP_TYPE_MASK = ((1UL << ms->__SWP_TYPE_BITS) - 1);
+ ms->__SWP_OFFSET_SHIFT = (ms->__SWP_TYPE_BITS +
ms->__SWP_TYPE_SHIFT);
+ ms->__SWP_OFFSET_BITS = 50;
+ ms->__SWP_OFFSET_MASK = ((1UL << ms->__SWP_OFFSET_BITS) - 1);
+ ms->PTE_PROT_NONE = (1UL << 58);
+ ms->PTE_FILE = 0; /* unused */
+ } else if (THIS_KERNEL_VERSION >= LINUX(4,0,0)) {
ms->__SWP_TYPE_BITS = 6;
ms->__SWP_TYPE_SHIFT = 2;
ms->__SWP_TYPE_MASK = ((1UL << ms->__SWP_TYPE_BITS) - 1);
--
2.39.0
--
Crash-utility mailing list
Crash-utility(a)redhat.com
https://listman.redhat.com/mailman/listinfo/crash-utility
Contribution Guidelines: https://github.com/crash-utility/crash/wiki
1 year, 1 month
Re: [Crash-utility] [PATCH 2/2] memory_driver: Support overriding kernel directory
by lijiang
Hi, Mathias
Thank you for the patchset.
On Tue, Sep 26, 2023 at 10:36 PM <crash-utility-request(a)redhat.com> wrote:
> Date: Tue, 26 Sep 2023 12:12:47 +0200
> From: Mathias Krause <minipli(a)grsecurity.net>
> To: crash-utility(a)redhat.com
> Subject: [Crash-utility] [PATCH 2/2] memory_driver: Support overriding
> kernel directory
> Message-ID: <20230926101247.1237748-3-minipli(a)grsecurity.net>
> Content-Type: text/plain; charset="US-ASCII"; x-default=true
>
> Support compiling the module against a different kernel version than the
> currently running one by allowing to set either KVER or KDIR variables
> on the make commandline.
>
> Also modernize the makefile slightly and make use of the kernel's
> 'clean' target to ensure to remove all generated files.
>
>
The [PATCH 1/2] looks good to me.
For the [PATCH 2/2], I only have two questions:
[1] With the patch 2/2, it always triggers recompiling the gdb like this:
# make lzo
TARGET: PPC64
CRASH: 8.0.3++
GDB: 10.2
CXX gdb.o
CXX ../../crash_target.o
CXX ada-exp.o
CXX ada-lang.o
CXX ada-tasks.o
CXX ada-typeprint.o
CXX ada-valprint.o
CXX ada-varobj.o
CXX addrmap.o
CXX agent.o
CXX alloc.o
CXX annotate.o
...
[2] With the patch 2/2, it always reports the following error "No such file
or directory", if the kernel-devel package is not installed.
# make clean
...
make -C /lib/modules/xxx/build M=/home/crash SUBDIRS=/home/crash clean ||
rm -f *.mod.c *.ko *.o Module.*
make[3]: *** /lib/modules/xxx/build: No such file or directory. Stop.
Actually, I did not build the crash.ko in the directory memory_driver/.
Is that expected behavior?
Thanks
Lianbo
> Signed-off-by: Mathias Krause <minipli(a)grsecurity.net>
> ---
> memory_driver/Makefile | 9 +++++++--
> 1 file changed, 7 insertions(+), 2 deletions(-)
>
> diff --git a/memory_driver/Makefile b/memory_driver/Makefile
> index b494aa3cd184..b720661fa75f 100644
> --- a/memory_driver/Makefile
> +++ b/memory_driver/Makefile
> @@ -8,8 +8,13 @@
> # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> # GNU General Public License for more details.
> #
> +ifneq ($(KERNELRELEASE),)
> obj-m := crash.o
> +else
> +KVER ?= $(shell uname -r)
> +KDIR ?= /lib/modules/${KVER}/build
> all:
> - make -C /lib/modules/`uname -r`/build M=${PWD} SUBDIRS=${PWD}
> modules
> + ${MAKE} -C ${KDIR} M=${PWD} SUBDIRS=${PWD} modules
> clean:
> - rm -f *.mod.c *.ko *.o Module.*
> + ${MAKE} -C ${KDIR} M=${PWD} SUBDIRS=${PWD} clean || ${RM} *.mod.c
> *.ko *.o Module.*
> +endif
> --
> 2.30.2
>
1 year, 1 month
[PATCH v2] ppc64: do page traversal if vmemmap_list not populated
by Aditya Gupta
Currently 'crash-tool' fails on vmcore collected on upstream kernel on
PowerPC64 with the error:
crash: invalid kernel virtual address: 0 type: "first list entry
Presently the address translation for vmemmap addresses is done using
the vmemmap_list. But with the below commit in Linux, vmemmap_list can
be empty, in case of Radix MMU on PowerPC64
368a0590d954: (powerpc/book3s64/vmemmap: switch radix to use a
different vmemmap handling function)
In case vmemmap_list is empty, then it's head is NULL, which crash tries
to access and fails due to accessing NULL.
Instead of depending on 'vmemmap_list' for address translation for
vmemmap addresses, do a kernel pagetable walk to get the physical
address associated with given virtual address
Tested-by: Sachin Sant <sachinp(a)linux.ibm.com>
Reviewed-by: Hari Bathini <hbathini(a)linux.ibm.com>
Signed-off-by: Aditya Gupta <adityag(a)linux.ibm.com>
---
Testing
=======
Git tree with patch applied:
https://github.com/adi-g15-ibm/crash/tree/bugzilla-203296-list-v2
This can be tested with '/proc/vmcore' as the vmcore, since makedumpfile
also fails in absence of 'vmemmap_list' in upstream linux
The fix for makedumpfile will also been sent to upstream
Changelog
=========
V2
+ handle the case of 'vmemmap_list' symbol missing according to reviews
---
---
ppc64.c | 51 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 35 insertions(+), 16 deletions(-)
diff --git a/ppc64.c b/ppc64.c
index fc34006f4863..cd98a679c45e 100644
--- a/ppc64.c
+++ b/ppc64.c
@@ -1280,10 +1280,32 @@ void ppc64_vmemmap_init(void)
long backing_size, virt_addr_offset, phys_offset, list_offset;
ulong *vmemmap_list;
char *vmemmap_buf;
- struct machine_specific *ms;
-
- if (!(kernel_symbol_exists("vmemmap_list")) ||
- !(kernel_symbol_exists("mmu_psize_defs")) ||
+ struct machine_specific *ms = machdep->machspec;
+
+ ld = &list_data;
+ BZERO(ld, sizeof(struct list_data));
+
+ /*
+ * vmemmap_list is missing or set to 0 in the kernel would imply
+ * vmemmap region is mapped in the kernel pagetable. So, read vmemmap_list
+ * anyway and use the translation method accordingly.
+ */
+ if (kernel_symbol_exists("vmemmap_list"))
+ readmem(symbol_value("vmemmap_list"), KVADDR, &ld->start,
+ sizeof(void *), "vmemmap_list", RETURN_ON_ERROR|QUIET);
+ if (!ld->start) {
+ /*
+ * vmemmap_list is set to 0 or missing. Do kernel pagetable walk
+ * for vmemmap address translation.
+ */
+ ms->vmemmap_list = NULL;
+ ms->vmemmap_cnt = 0;
+
+ machdep->flags |= VMEMMAP_AWARE;
+ return;
+ }
+
+ if (!(kernel_symbol_exists("mmu_psize_defs")) ||
!(kernel_symbol_exists("mmu_vmemmap_psize")) ||
!STRUCT_EXISTS("vmemmap_backing") ||
!STRUCT_EXISTS("mmu_psize_def") ||
@@ -1293,8 +1315,6 @@ void ppc64_vmemmap_init(void)
!MEMBER_EXISTS("vmemmap_backing", "list"))
return;
- ms = machdep->machspec;
-
backing_size = STRUCT_SIZE("vmemmap_backing");
virt_addr_offset = MEMBER_OFFSET("vmemmap_backing", "virt_addr");
phys_offset = MEMBER_OFFSET("vmemmap_backing", "phys");
@@ -1313,14 +1333,8 @@ void ppc64_vmemmap_init(void)
ms->vmemmap_psize = 1 << shift;
- ld = &list_data;
- BZERO(ld, sizeof(struct list_data));
- if (!readmem(symbol_value("vmemmap_list"),
- KVADDR, &ld->start, sizeof(void *), "vmemmap_list",
- RETURN_ON_ERROR))
- return;
- ld->end = symbol_value("vmemmap_list");
- ld->list_head_offset = list_offset;
+ ld->end = symbol_value("vmemmap_list");
+ ld->list_head_offset = list_offset;
hq_open();
cnt = do_list(ld);
@@ -1366,7 +1380,7 @@ ppc64_vmemmap_to_phys(ulong kvaddr, physaddr_t *paddr, int verbose)
{
int i;
ulong offset;
- struct machine_specific *ms;
+ struct machine_specific *ms = machdep->machspec;
if (!(machdep->flags & VMEMMAP_AWARE)) {
/*
@@ -1386,7 +1400,12 @@ ppc64_vmemmap_to_phys(ulong kvaddr, physaddr_t *paddr, int verbose)
return FALSE;
}
- ms = machdep->machspec;
+ /**
+ * When vmemmap_list is not populated, kernel does the mapping in init_mm
+ * page table, so do a pagetable walk in kernel page table
+ */
+ if (!ms->vmemmap_list)
+ return ppc64_vtop_level4(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
for (i = 0; i < ms->vmemmap_cnt; i++) {
if ((kvaddr >= ms->vmemmap_list[i].virt) &&
--
2.41.0
1 year, 2 months
Re: [Crash-utility] [PATCH] Fix "vtop" command to display the swapinfo for arm64 kernel 6.1+
by lijiang
Hi, Cheng
Thank you for the fix.
On Thu, Sep 14, 2023 at 4:05 PM <crash-utility-request(a)redhat.com> wrote:
> Date: Thu, 14 Sep 2023 07:51:31 +0000
> From: ??? <chenguanyou(a)xiaomi.com>
> To: "crash-utility(a)redhat.com" <crash-utility(a)redhat.com>
> Cc: "lijiang(a)redhat.com" <lijiang(a)redhat.com>, "k-hagio-ab(a)nec.com"
> <k-hagio-ab(a)nec.com>
> Subject: [Crash-utility] [PATCH] Fix "vtop" command to display the
> swapinfo for arm64 kernel 6.1+
> Message-ID: <bba8281d4e4f41308ce095c89fe04d1c(a)xiaomi.com>
> Content-Type: text/plain; charset="gb2312"
>
> #define __SWP_TYPE_SHIFT 3
> #define __SWP_TYPE_BITS 5
>
>
Can you help to add the related kernel commit to the patch log? For this
one, it should be the following commit:
570ef363509b ("arm64/pgtable: support __HAVE_ARCH_PTE_SWP_EXCLUSIVE")
Before:
> crash> vtop 70504000
> VIRTUAL PHYSICAL
> 70504000 (not mapped)
>
> PAGE DIRECTORY: ffffff80f265c000
> PGD: ffffff80f265c008 => 800000141537003
> PMD: ffffff8101537c10 => 800000141538003
> PTE: ffffff8101538820 => 12bc3e04
>
> PTE vtop: cannot determine swap location
>
> After:
> crash> vtop 70504000
> VIRTUAL PHYSICAL
> 70504000 (not mapped)
>
> PAGE DIRECTORY: ffffff80f265c000
> PGD: ffffff80f265c008 => 800000141537003
> PMD: ffffff8101537c10 => 800000141538003
> PTE: ffffff8101538820 => 12bc3e04
>
> PTE SWAP OFFSET
> 12bc3e04 /first_stage_ramdisk/dev/block/zram0 1227838
>
> VMA START END FLAGS FILE
> ffffff80dfe7b578 70504000 707bd000 100073
>
> SWAP: /first_stage_ramdisk/dev/block/zram0 OFFSET: 1227838
>
> Signed-off-by: chenguanyou <chenguanyou(a)xiaomi.com>
> ---
> arm64.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/arm64.c b/arm64.c
> index 39d5f04..ea1b51b 100644
> --- a/arm64.c
> +++ b/arm64.c
> @@ -468,8 +468,16 @@ arm64_init(int when)
> }
> }
>
> -
> - if (THIS_KERNEL_VERSION >= LINUX(4,0,0)) {
> + if (THIS_KERNEL_VERSION >= LINUX(6,1,0)) {
>
In addition, the related kernel change was done in v5.19-rc1, not in the
linux 6.1.0.
$ git describe --contains 570ef363509b031966ed669fa002c8441dff273c
v5.19-rc1~138^2~226
So it should be the '>=LINUX(5,19,0)', can you also help to confirm this
one?
Thanks
Lianbo
+ ms->__SWP_TYPE_BITS = 5;
> + ms->__SWP_TYPE_SHIFT = 3;
> + ms->__SWP_TYPE_MASK = ((1UL << ms->__SWP_TYPE_BITS) - 1);
> + ms->__SWP_OFFSET_SHIFT = (ms->__SWP_TYPE_BITS +
> ms->__SWP_TYPE_SHIFT);
> + ms->__SWP_OFFSET_BITS = 50;
> + ms->__SWP_OFFSET_MASK = ((1UL << ms->__SWP_OFFSET_BITS) - 1);
> + ms->PTE_PROT_NONE = (1UL << 58);
> + ms->PTE_FILE = 0; /* unused */
> + } else if (THIS_KERNEL_VERSION >= LINUX(4,0,0)) {
> ms->__SWP_TYPE_BITS = 6;
> ms->__SWP_TYPE_SHIFT = 2;
> ms->__SWP_TYPE_MASK = ((1UL << ms->__SWP_TYPE_BITS) - 1);
> --
> 2.39.0
>
1 year, 2 months
[PATCH 0/2] memory_driver: grsecurity compatibility
by Mathias Krause
This small series makes the memory_driver kernel module compatible with
a recent change to the grsecurity Linux kernel patch as well as allowing
to override the kernel's build directory.
Please apply!
Thanks,
Mathias
Mathias Krause (2):
memory_driver: Use designated initializer for 'crash_dev'
memory_driver: Support overriding kernel directory
memory_driver/Makefile | 9 +++++++--
memory_driver/crash.c | 6 +++---
2 files changed, 10 insertions(+), 5 deletions(-)
--
2.30.2
1 year, 2 months